musora-content-services 2.30.5 → 2.31.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.
- 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/docs.js.yml +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 +54 -0
- package/README.md +0 -0
- package/babel.config.cjs +0 -0
- package/docs/ContentOrganization.html +0 -0
- package/docs/Gamification.html +0 -0
- package/docs/UserManagementSystem.html +0 -0
- package/docs/api_types.js.html +0 -0
- package/docs/config.js.html +0 -0
- package/docs/content-org_content-org.js.html +0 -0
- package/docs/content-org_playlists-types.js.html +0 -0
- package/docs/content-org_playlists.js.html +0 -0
- package/docs/content.js.html +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/gamification_awards.js.html +0 -0
- package/docs/gamification_gamification.js.html +0 -0
- package/docs/gamification_types.js.html +0 -0
- package/docs/global.html +0 -0
- package/docs/index.html +0 -0
- package/docs/module-Awards.html +0 -0
- package/docs/module-Config.html +0 -0
- package/docs/module-Content-Services-V2.html +0 -0
- package/docs/module-Interests.html +0 -0
- package/docs/module-Permissions.html +0 -0
- package/docs/module-Playlists.html +0 -0
- package/docs/module-Railcontent-Services.html +0 -0
- package/docs/module-Sanity-Services.html +0 -0
- package/docs/module-Sessions.html +0 -0
- package/docs/module-UserActivity.html +0 -0
- package/docs/module-UserChat.html +0 -0
- package/docs/module-UserManagement.html +0 -0
- package/docs/module-UserNotifications.html +0 -0
- package/docs/module-UserProfile.html +0 -0
- package/docs/railcontent.js.html +0 -0
- package/docs/sanity.js.html +0 -0
- package/docs/scripts/collapse.js +0 -0
- package/docs/scripts/commonNav.js +0 -0
- package/docs/scripts/linenumber.js +0 -0
- package/docs/scripts/nav.js +0 -0
- package/docs/scripts/polyfill.js +0 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
- package/docs/scripts/prettify/lang-css.js +0 -0
- package/docs/scripts/prettify/prettify.js +0 -0
- package/docs/scripts/search.js +0 -0
- package/docs/styles/jsdoc.css +0 -0
- package/docs/styles/prettify.css +0 -0
- package/docs/userActivity.js.html +0 -0
- package/docs/user_chat.js.html +0 -0
- package/docs/user_interests.js.html +0 -0
- package/docs/user_management.js.html +0 -0
- package/docs/user_notifications.js.html +0 -0
- package/docs/user_permissions.js.html +0 -0
- package/docs/user_profile.js.html +0 -0
- package/docs/user_sessions.js.html +0 -0
- package/docs/user_types.js.html +0 -0
- package/docs/user_user-management-system.js.html +0 -0
- package/jest.config.js +0 -0
- package/jsdoc.json +0 -0
- package/package.json +1 -1
- package/src/contentMetaData.js +6 -0
- package/src/contentTypeConfig.js +7 -3
- package/src/filterBuilder.js +12 -1
- package/src/index.d.ts +11 -1
- package/src/index.js +12 -2
- 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/services/api/types.js +0 -0
- package/src/services/config.js +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/playlists-types.js +0 -0
- package/src/services/content-org/playlists.js +0 -0
- package/src/services/content.js +18 -8
- package/src/services/contentAggregator.js +5 -4
- package/src/services/contentLikes.js +0 -0
- package/src/services/contentProgress.js +0 -0
- package/src/services/dataContext.js +0 -0
- package/src/services/dateUtils.js +0 -0
- package/src/services/forum.js +0 -0
- package/src/services/gamification/awards.js +0 -0
- package/src/services/gamification/gamification.js +0 -0
- package/src/services/gamification/types.js +0 -0
- package/src/services/imageSRCBuilder.js +0 -0
- package/src/services/imageSRCVerify.js +0 -0
- package/src/services/railcontent.js +20 -0
- package/src/services/recommendations.js +0 -0
- package/src/services/sanity.js +64 -56
- 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/notifications.js +25 -3
- 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.js +0 -0
- package/src/services/user/user-management-system.js +0 -0
- package/src/services/userActivity.js +31 -18
- 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/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/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
|
@@ -77,12 +77,13 @@ export async function addContextToContent(dataPromise, ...dataArgs)
|
|
|
77
77
|
const dataParam = lastArg === options ? dataArgs.slice(0, -1) : dataArgs
|
|
78
78
|
|
|
79
79
|
let data = await dataPromise(...dataParam)
|
|
80
|
-
if(!data) return false
|
|
81
80
|
const isDataAnArray = Array.isArray(data)
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if(isDataAnArray && data.length === 0) return data
|
|
82
|
+
if(!data) return false
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
const items = extractItemsFromData(data, dataField, isDataAnArray, dataField_includeParent) ?? []
|
|
85
|
+
const ids = items.map(item => item?.id).filter(Boolean)
|
|
86
|
+
if(ids.length === 0) return data
|
|
86
87
|
|
|
87
88
|
const [progressData, isLikedData, resumeTimeData, lastInteractedChildData, nextLessonData, navigateToData] = await Promise.all([
|
|
88
89
|
addProgressPercentage || addProgressStatus || addProgressTimestamp ? getProgressDateByIds(ids) : Promise.resolve(null),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/services/forum.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -378,16 +378,36 @@ export async function fetchUserBadges(brand = null) {
|
|
|
378
378
|
return await fetchHandler(url, 'get')
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
+
/**
|
|
382
|
+
* complete a content's progress for a given user
|
|
383
|
+
* @param contentId
|
|
384
|
+
* @returns {Promise<any|string|null>}
|
|
385
|
+
*/
|
|
381
386
|
export async function postContentComplete(contentId) {
|
|
382
387
|
let url = `/api/content/v1/user/progress/complete/${contentId}`
|
|
383
388
|
return postDataHandler(url)
|
|
384
389
|
}
|
|
385
390
|
|
|
391
|
+
/**
|
|
392
|
+
* resets the user's progress on a content
|
|
393
|
+
* @param contentId
|
|
394
|
+
* @returns {Promise<any|string|null>}
|
|
395
|
+
*/
|
|
386
396
|
export async function postContentReset(contentId) {
|
|
387
397
|
let url = `/api/content/v1/user/progress/reset/${contentId}`
|
|
388
398
|
return postDataHandler(url)
|
|
389
399
|
}
|
|
390
400
|
|
|
401
|
+
/**
|
|
402
|
+
* restores the user's progress on a content
|
|
403
|
+
* @param contentId
|
|
404
|
+
* @returns {Promise<any|string|null>}
|
|
405
|
+
*/
|
|
406
|
+
export async function postContentRestore(contentId) {
|
|
407
|
+
let url = `/api/content/v1/user/progress/restore/${contentId}`
|
|
408
|
+
return postDataHandler(url)
|
|
409
|
+
}
|
|
410
|
+
|
|
391
411
|
/**
|
|
392
412
|
* Set a user's StudentView Flag
|
|
393
413
|
*
|
|
File without changes
|
package/src/services/sanity.js
CHANGED
|
@@ -476,7 +476,7 @@ export async function fetchByRailContentId(id, contentType) {
|
|
|
476
476
|
* .then(contents => console.log(contents))
|
|
477
477
|
* .catch(error => console.error(error));
|
|
478
478
|
*/
|
|
479
|
-
export async function fetchByRailContentIds(ids, contentType = undefined, brand = undefined) {
|
|
479
|
+
export async function fetchByRailContentIds(ids, contentType = undefined, brand = undefined, includePermissionsAndStatusFilter = false) {
|
|
480
480
|
if (!ids?.length) {
|
|
481
481
|
return []
|
|
482
482
|
}
|
|
@@ -485,8 +485,10 @@ export async function fetchByRailContentIds(ids, contentType = undefined, brand
|
|
|
485
485
|
const brandFilter = brand ? ` && brand == "${brand}"` : ''
|
|
486
486
|
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {pullFutureContent: true}).buildFilter()
|
|
487
487
|
const fields = await getFieldsForContentTypeWithFilteredChildren(contentType, true)
|
|
488
|
+
const baseFilter = `railcontent_id in [${idsString}]${brandFilter}`
|
|
489
|
+
const finalFilter = includePermissionsAndStatusFilter ? await new FilterBuilder(baseFilter).buildFilter() : baseFilter
|
|
488
490
|
const query = `*[
|
|
489
|
-
|
|
491
|
+
${finalFilter}
|
|
490
492
|
]{
|
|
491
493
|
${fields}
|
|
492
494
|
'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
|
|
@@ -535,7 +537,9 @@ export async function fetchContentRows(brand, pageName, contentRowSlug)
|
|
|
535
537
|
name,
|
|
536
538
|
'slug': slug.current,
|
|
537
539
|
'content': content[${childFilter}]->{
|
|
538
|
-
'children': child[${childFilter}]->{ 'id': railcontent_id,
|
|
540
|
+
'children': child[${childFilter}]->{ 'id': railcontent_id,
|
|
541
|
+
'type': _type, brand, 'thumbnail': thumbnail.asset->url,
|
|
542
|
+
'children': child[${childFilter}]->{'id': railcontent_id}, },
|
|
539
543
|
${getFieldsForContentType('tab-data')}
|
|
540
544
|
'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
|
|
541
545
|
},
|
|
@@ -749,7 +753,7 @@ export function getSortOrder(sort = '-published_on', brand, groupBy) {
|
|
|
749
753
|
sort = isDesc ? sort.substring(1) : sort
|
|
750
754
|
switch (sort) {
|
|
751
755
|
case 'slug':
|
|
752
|
-
sortOrder = groupBy ? 'name' : 'title'
|
|
756
|
+
sortOrder = groupBy ? 'name' : '!defined(title), lower(title)'
|
|
753
757
|
break
|
|
754
758
|
case 'name':
|
|
755
759
|
sortOrder = sort
|
|
@@ -1089,6 +1093,7 @@ export async function jumpToContinueContent(railcontentId) {
|
|
|
1089
1093
|
/**
|
|
1090
1094
|
* Fetch the page data for a specific lesson by Railcontent ID.
|
|
1091
1095
|
* @param {string} railContentId - The Railcontent ID of the current lesson.
|
|
1096
|
+
* @parent {boolean} addParent - Whether to include parent content data in the response.
|
|
1092
1097
|
* @returns {Promise<Object|null>} - The fetched page data or null if found.
|
|
1093
1098
|
*
|
|
1094
1099
|
* @example
|
|
@@ -1096,44 +1101,51 @@ export async function jumpToContinueContent(railcontentId) {
|
|
|
1096
1101
|
* .then(data => console.log(data))
|
|
1097
1102
|
* .catch(error => console.error(error));
|
|
1098
1103
|
*/
|
|
1099
|
-
export async function fetchLessonContent(railContentId) {
|
|
1104
|
+
export async function fetchLessonContent(railContentId, { addParent = false } = {}) {
|
|
1100
1105
|
const filterParams = { isSingle: true, pullFutureContent: true }
|
|
1101
1106
|
|
|
1107
|
+
const parentQuery = addParent
|
|
1108
|
+
? `"parent_content_data": *[railcontent_id in [...(^.parent_content_data[].id)]]{
|
|
1109
|
+
"id": railcontent_id,
|
|
1110
|
+
title,
|
|
1111
|
+
slug,
|
|
1112
|
+
"type": _type,
|
|
1113
|
+
"logo" : logo_image_url.asset->url,
|
|
1114
|
+
"dark_mode_logo": dark_mode_logo_url.asset->url,
|
|
1115
|
+
"light_mode_logo": light_mode_logo_url.asset->url,
|
|
1116
|
+
"badge": badge[0]->badge.asset->url,
|
|
1117
|
+
},`
|
|
1118
|
+
: ''
|
|
1119
|
+
|
|
1102
1120
|
const fields = `${getFieldsForContentType()}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
"live_event_end_time": live_event_end_time,
|
|
1132
|
-
"live_event_youtube_id": live_event_youtube_id,
|
|
1133
|
-
"videoId": coalesce(live_event_youtube_id, video.external_id),
|
|
1134
|
-
"live_event_is_global": live_global_event == true
|
|
1135
|
-
}
|
|
1136
|
-
)`
|
|
1121
|
+
"resources": ${resourcesField},
|
|
1122
|
+
soundslice,
|
|
1123
|
+
instrumentless,
|
|
1124
|
+
soundslice_slug,
|
|
1125
|
+
"description": ${descriptionField},
|
|
1126
|
+
"chapters": ${chapterField},
|
|
1127
|
+
"instructors":instructor[]->name,
|
|
1128
|
+
"instructor": ${instructorField},
|
|
1129
|
+
${assignmentsField}
|
|
1130
|
+
video,
|
|
1131
|
+
length_in_seconds,
|
|
1132
|
+
mp3_no_drums_no_click_url,
|
|
1133
|
+
mp3_no_drums_yes_click_url,
|
|
1134
|
+
mp3_yes_drums_no_click_url,
|
|
1135
|
+
mp3_yes_drums_yes_click_url,
|
|
1136
|
+
"permission_id": permission[]->railcontent_id,
|
|
1137
|
+
${parentQuery}
|
|
1138
|
+
...select(
|
|
1139
|
+
defined(live_event_start_time) => {
|
|
1140
|
+
"live_event_start_time": live_event_start_time,
|
|
1141
|
+
"live_event_end_time": live_event_end_time,
|
|
1142
|
+
"live_event_youtube_id": live_event_youtube_id,
|
|
1143
|
+
"videoId": coalesce(live_event_youtube_id, video.external_id),
|
|
1144
|
+
"live_event_is_global": live_global_event == true
|
|
1145
|
+
}
|
|
1146
|
+
)
|
|
1147
|
+
`
|
|
1148
|
+
|
|
1137
1149
|
const query = await buildQuery(`railcontent_id == ${railContentId}`, filterParams, fields, {
|
|
1138
1150
|
isSingle: true,
|
|
1139
1151
|
})
|
|
@@ -1166,7 +1178,7 @@ export async function fetchLessonContent(railContentId) {
|
|
|
1166
1178
|
export async function fetchRelatedRecommendedContent(railContentId, brand, count = 10) {
|
|
1167
1179
|
const recommendedItems = await fetchSimilarItems(railContentId, brand, count)
|
|
1168
1180
|
if (recommendedItems && recommendedItems.length > 0) {
|
|
1169
|
-
return fetchByRailContentIds(recommendedItems)
|
|
1181
|
+
return fetchByRailContentIds(recommendedItems, 'tab-data', brand, true)
|
|
1170
1182
|
}
|
|
1171
1183
|
|
|
1172
1184
|
return await fetchRelatedLessons(railContentId, brand).then((result) =>
|
|
@@ -1239,20 +1251,26 @@ async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, cou
|
|
|
1239
1251
|
* @param {string} brand - The current brand.
|
|
1240
1252
|
* @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
|
|
1241
1253
|
*/
|
|
1242
|
-
export async function fetchSiblingContent(railContentId, brand)
|
|
1254
|
+
export async function fetchSiblingContent(railContentId, brand= null)
|
|
1243
1255
|
{
|
|
1244
1256
|
const filterGetParent = await new FilterBuilder(`references(^._id) && _type == ^.parent_type`, {
|
|
1257
|
+
pullFutureContent: true
|
|
1258
|
+
}).buildFilter()
|
|
1259
|
+
const filterForParentList = await new FilterBuilder(`references(^._id) && _type == ^.parent_type`, {
|
|
1245
1260
|
pullFutureContent: true,
|
|
1261
|
+
isParentFilter: true,
|
|
1246
1262
|
}).buildFilter()
|
|
1263
|
+
|
|
1247
1264
|
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
1248
1265
|
|
|
1266
|
+
const brandString = brand ? ` && brand == "${brand}"` : ''
|
|
1249
1267
|
const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, artist->, "permission_id": permission[]->railcontent_id, "genre": genre[]->name, "parent_id": parent_content_data[0].id`
|
|
1250
1268
|
|
|
1251
|
-
const query = `*[railcontent_id == ${railContentId}
|
|
1269
|
+
const query = `*[railcontent_id == ${railContentId}${brandString}]{
|
|
1252
1270
|
_type, parent_type, 'parent_id': parent_content_data[0].id, railcontent_id,
|
|
1253
1271
|
'for-calculations': *[${filterGetParent}][0]{
|
|
1254
1272
|
'siblings-list': child[]->railcontent_id,
|
|
1255
|
-
'parents-list': *[${
|
|
1273
|
+
'parents-list': *[${filterForParentList}][0].child[]->railcontent_id
|
|
1256
1274
|
},
|
|
1257
1275
|
"related_lessons" : *[${filterGetParent}][0].child[${childrenFilter}]->{${queryFields}}
|
|
1258
1276
|
}`
|
|
@@ -2157,7 +2175,6 @@ export async function fetchTabData(
|
|
|
2157
2175
|
) {
|
|
2158
2176
|
const start = (page - 1) * limit
|
|
2159
2177
|
const end = start + limit
|
|
2160
|
-
let withoutPagination = false
|
|
2161
2178
|
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
2162
2179
|
const includedFieldsFilter =
|
|
2163
2180
|
includedFields.length > 0 ? filtersToGroq(includedFields, [], pageName) : ''
|
|
@@ -2168,27 +2185,19 @@ export async function fetchTabData(
|
|
|
2168
2185
|
case 'recent':
|
|
2169
2186
|
progressIds = await getAllStartedOrCompleted({ brand, onlyIds: true });
|
|
2170
2187
|
sortOrder = null;
|
|
2171
|
-
withoutPagination = true;
|
|
2172
2188
|
break;
|
|
2173
2189
|
case 'incomplete':
|
|
2174
2190
|
progressIds = await getAllStarted();
|
|
2175
2191
|
sortOrder = null;
|
|
2176
|
-
withoutPagination = true;
|
|
2177
2192
|
break;
|
|
2178
2193
|
case 'completed':
|
|
2179
2194
|
progressIds = await getAllCompleted();
|
|
2180
2195
|
sortOrder = null;
|
|
2181
|
-
withoutPagination = true;
|
|
2182
2196
|
break;
|
|
2183
2197
|
}
|
|
2184
2198
|
|
|
2185
2199
|
// limits the results to supplied progressIds for started & completed filters
|
|
2186
2200
|
const progressFilter = await getProgressFilter(progress, progressIds)
|
|
2187
|
-
if (sort === "recommended"){
|
|
2188
|
-
progressIds = await recommendations(brand);
|
|
2189
|
-
withoutPagination = true;
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
2201
|
const fieldsString = getFieldsForContentType('tab-data');
|
|
2193
2202
|
const now = getSanityDate(new Date())
|
|
2194
2203
|
|
|
@@ -2202,7 +2211,7 @@ export async function fetchTabData(
|
|
|
2202
2211
|
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`).buildFilter()
|
|
2203
2212
|
entityFieldsString =
|
|
2204
2213
|
` ${fieldsString}
|
|
2205
|
-
'children': child[${childrenFilter}]->{'id': railcontent_id},
|
|
2214
|
+
'children': child[${childrenFilter}]->{'id': railcontent_id, 'type': _type, brand, 'thumbnail': thumbnail.asset->url},
|
|
2206
2215
|
'isLive': live_event_start_time <= "${now}" && live_event_end_time >= "${now}",
|
|
2207
2216
|
'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
|
|
2208
2217
|
'length_in_seconds': coalesce(
|
|
@@ -2217,13 +2226,12 @@ export async function fetchTabData(
|
|
|
2217
2226
|
query = buildEntityAndTotalQuery(filterWithRestrictions, entityFieldsString, {
|
|
2218
2227
|
sortOrder: sortOrder,
|
|
2219
2228
|
start: start,
|
|
2220
|
-
end: end
|
|
2221
|
-
withoutPagination: withoutPagination,
|
|
2229
|
+
end: end
|
|
2222
2230
|
})
|
|
2223
2231
|
|
|
2224
2232
|
let results = await fetchSanity(query, true);
|
|
2225
2233
|
|
|
2226
|
-
if (
|
|
2234
|
+
if (['recent', 'incomplete', 'completed'].includes(progress) && results.entity.length > 1) {
|
|
2227
2235
|
const orderMap = new Map(progressIds.map((id, index) => [id, index]))
|
|
2228
2236
|
results.entity = results.entity
|
|
2229
2237
|
.sort((a, b) => {
|
package/src/services/types.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -114,11 +114,33 @@ export async function deleteNotification(notificationId) {
|
|
|
114
114
|
if (!notificationId) {
|
|
115
115
|
throw new Error('notificationId is required')
|
|
116
116
|
}
|
|
117
|
-
|
|
118
117
|
const url = `${baseUrl}/v1/${notificationId}`
|
|
119
118
|
return fetchHandler(url, 'delete')
|
|
120
119
|
}
|
|
121
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Restores a specific notification.
|
|
123
|
+
*
|
|
124
|
+
* @param {number} notificationId - The ID of the notification to restore.
|
|
125
|
+
*
|
|
126
|
+
* @returns {Promise<any>} - A promise that resolves when the notification is successfully restored.
|
|
127
|
+
*
|
|
128
|
+
* @throws {Error} - Throws an error if notificationId is not provided.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* restoreNotification(123)
|
|
132
|
+
* .then(response => console.log(response))
|
|
133
|
+
* .catch(error => console.error(error));
|
|
134
|
+
*/
|
|
135
|
+
export async function restoreNotification(notificationId) {
|
|
136
|
+
if (!notificationId) {
|
|
137
|
+
throw new Error('notificationId is required')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const url = `${baseUrl}/v1/${notificationId}`
|
|
141
|
+
return fetchHandler(url, 'put')
|
|
142
|
+
}
|
|
143
|
+
|
|
122
144
|
/**
|
|
123
145
|
* Fetches the count of unread notifications for the current user in a given brand context.
|
|
124
146
|
*
|
|
@@ -140,7 +162,7 @@ export async function deleteNotification(notificationId) {
|
|
|
140
162
|
export async function fetchUnreadCount({ brand = 'drumeo'} = {}) {
|
|
141
163
|
const url = `${baseUrl}/v1/unread-count`
|
|
142
164
|
const notifUnread = await fetchHandler(url, 'get')
|
|
143
|
-
if (notifUnread.data > 0) {
|
|
165
|
+
if (notifUnread && notifUnread.data > 0) {
|
|
144
166
|
return notifUnread// Return early if unread notifications exist
|
|
145
167
|
}
|
|
146
168
|
const liveEventPollingState = await fetchLiveEventPollingState()
|
|
@@ -148,7 +170,7 @@ export async function fetchUnreadCount({ brand = 'drumeo'} = {}) {
|
|
|
148
170
|
const liveEvent = await fetchLiveEvent(brand)
|
|
149
171
|
return { data: liveEvent ? 1 : 0}
|
|
150
172
|
}
|
|
151
|
-
return
|
|
173
|
+
return { data: 0}
|
|
152
174
|
}
|
|
153
175
|
|
|
154
176
|
/**
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
fetchRecentUserActivities,
|
|
12
12
|
} from './railcontent'
|
|
13
13
|
import { DataContext, UserActivityVersionKey } from './dataContext.js'
|
|
14
|
-
import { fetchByRailContentIds, fetchShows } from './sanity'
|
|
14
|
+
import { fetchByRailContentId, fetchByRailContentIds, fetchShows } from './sanity'
|
|
15
15
|
import { fetchPlaylist, fetchUserPlaylists } from './content-org/playlists'
|
|
16
16
|
import { pinnedGuidedCourses } from './content-org/guided-courses'
|
|
17
17
|
import {
|
|
@@ -930,6 +930,22 @@ export async function deleteUserActivity(id) {
|
|
|
930
930
|
return await fetchHandler(url, 'DELETE')
|
|
931
931
|
}
|
|
932
932
|
|
|
933
|
+
/**
|
|
934
|
+
* Restores a specific user activity by its ID.
|
|
935
|
+
*
|
|
936
|
+
* @param {number|string} id - The ID of the user activity to restore.
|
|
937
|
+
* @returns {Promise<Object>} - A promise that resolves to the API response after restoration.
|
|
938
|
+
*
|
|
939
|
+
* @example
|
|
940
|
+
* restoreUserActivity(789)
|
|
941
|
+
* .then(response => console.log('Restored:', response))
|
|
942
|
+
* .catch(error => console.error(error));
|
|
943
|
+
*/
|
|
944
|
+
export async function restoreUserActivity(id) {
|
|
945
|
+
const url = `/api/user-management-system/v1/activities/${id}`
|
|
946
|
+
return await fetchHandler(url, 'POST')
|
|
947
|
+
}
|
|
948
|
+
|
|
933
949
|
async function extractPinnedItemsAndSortAllItems(
|
|
934
950
|
userPinnedItem,
|
|
935
951
|
contentsMap,
|
|
@@ -1149,24 +1165,21 @@ async function processContentItem(content) {
|
|
|
1149
1165
|
}
|
|
1150
1166
|
|
|
1151
1167
|
return {
|
|
1152
|
-
id:
|
|
1153
|
-
progressType:
|
|
1154
|
-
header:
|
|
1155
|
-
pinned:
|
|
1156
|
-
content:
|
|
1157
|
-
body:
|
|
1168
|
+
id: content.id,
|
|
1169
|
+
progressType: 'content',
|
|
1170
|
+
header: contentType,
|
|
1171
|
+
pinned: content.pinned ?? false,
|
|
1172
|
+
content: content,
|
|
1173
|
+
body: {
|
|
1158
1174
|
progressPercent: isLive ? undefined : content.progressPercentage,
|
|
1159
|
-
thumbnail:
|
|
1160
|
-
title:
|
|
1161
|
-
isLive:
|
|
1162
|
-
badge:
|
|
1163
|
-
isLocked:
|
|
1164
|
-
subtitle:
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
? `${content.progressPercentage}% Complete`
|
|
1168
|
-
: `${content.difficulty_string} • ${content.artist_name}`
|
|
1169
|
-
: `${content.completed_children} of ${content.lesson_count ?? content.child_count} Lessons Complete`,
|
|
1175
|
+
thumbnail: content.thumbnail,
|
|
1176
|
+
title: content.title,
|
|
1177
|
+
isLive: isLive,
|
|
1178
|
+
badge: content.badge ?? null,
|
|
1179
|
+
isLocked: content.is_locked ?? false,
|
|
1180
|
+
subtitle: collectionLessonTypes.includes(content.type) || content.lesson_count > 1
|
|
1181
|
+
? `${content.completed_children} of ${content.lesson_count ?? content.child_count} Lessons Complete`
|
|
1182
|
+
: (contentType === 'lesson' && isLive === false) ? `${content.progressPercentage}% Complete`: `${content.difficulty_string} • ${content.artist_name}`
|
|
1170
1183
|
},
|
|
1171
1184
|
cta: {
|
|
1172
1185
|
text: ctaText,
|
package/test/HttpClient.test.js
CHANGED
|
File without changes
|
package/test/content.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/dataContext.test.js
CHANGED
|
File without changes
|
package/test/forum.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/initializeTests.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/localStorageMock.js
CHANGED
|
File without changes
|
package/test/log.js
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/tools/generate-index.cjs
CHANGED
|
File without changes
|