musora-content-services 2.79.1 → 2.80.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/CHANGELOG.md +7 -0
- package/docs/Content.html +269 -0
- package/docs/ContentOrganization.html +2 -2
- package/docs/Forums.html +2 -2
- package/docs/Gamification.html +2 -2
- package/docs/TestUser.html +2 -2
- package/docs/UserManagementSystem.html +2 -2
- package/docs/api_types.js.html +2 -2
- package/docs/config.js.html +2 -2
- package/docs/content-org_content-org.js.html +2 -2
- package/docs/content-org_guided-courses.ts.html +2 -2
- package/docs/content-org_learning-paths.ts.html +21 -14
- package/docs/content-org_playlists-types.js.html +2 -2
- package/docs/content-org_playlists.js.html +2 -2
- package/docs/content.js.html +2 -2
- package/docs/content_artist.ts.html +212 -0
- package/docs/content_content.ts.html +77 -0
- package/docs/content_genre.ts.html +211 -0
- package/docs/content_instructor.ts.html +203 -0
- package/docs/forums_categories.ts.html +3 -3
- package/docs/forums_forums.ts.html +2 -2
- package/docs/forums_posts.ts.html +2 -2
- package/docs/forums_threads.ts.html +2 -2
- package/docs/gamification_awards.ts.html +2 -2
- package/docs/gamification_gamification.js.html +2 -2
- package/docs/global.html +2 -2
- package/docs/index.html +2 -2
- package/docs/liveTesting.ts.html +2 -2
- package/docs/module-Accounts.html +2 -2
- package/docs/module-Artist.html +991 -0
- package/docs/module-Awards.html +2 -2
- package/docs/module-Config.html +2 -2
- package/docs/module-Content-Services-V2.html +2 -2
- package/docs/module-Forums.html +2 -2
- package/docs/module-Genre.html +981 -0
- package/docs/module-GuidedCourses.html +2 -2
- package/docs/module-Instructor.html +929 -0
- package/docs/module-Interests.html +2 -2
- package/docs/module-LearningPaths.html +6 -6
- package/docs/module-Onboarding.html +2 -2
- package/docs/module-Payments.html +2 -2
- package/docs/module-Permissions.html +2 -2
- package/docs/module-Playlists.html +2 -2
- package/docs/module-ProgressRow.html +2 -2
- package/docs/module-Railcontent-Services.html +2 -2
- package/docs/module-Sanity-Services.html +326 -1854
- package/docs/module-Sessions.html +2 -2
- package/docs/module-UserActivity.html +2 -2
- package/docs/module-UserChat.html +2 -2
- package/docs/module-UserManagement.html +2 -2
- package/docs/module-UserMemberships.html +2 -2
- package/docs/module-UserNotifications.html +2 -2
- package/docs/module-UserProfile.html +2 -2
- package/docs/progress-row_method-card.js.html +2 -2
- package/docs/railcontent.js.html +2 -2
- package/docs/sanity.js.html +107 -268
- package/docs/userActivity.js.html +2 -2
- package/docs/user_account.ts.html +2 -2
- package/docs/user_chat.js.html +2 -2
- package/docs/user_interests.js.html +2 -2
- package/docs/user_management.js.html +2 -2
- package/docs/user_memberships.ts.html +2 -2
- package/docs/user_notifications.js.html +2 -2
- package/docs/user_onboarding.ts.html +2 -2
- package/docs/user_payments.ts.html +2 -2
- package/docs/user_permissions.js.html +2 -2
- package/docs/user_profile.js.html +2 -2
- package/docs/user_sessions.js.html +2 -2
- package/docs/user_types.js.html +2 -2
- package/docs/user_user-management-system.js.html +2 -2
- package/jsdoc.json +1 -0
- package/package.json +1 -1
- package/src/contentTypeConfig.js +2 -2
- package/src/index.d.ts +28 -5
- package/src/index.js +28 -5
- package/src/services/content/artist.ts +139 -0
- package/src/services/content/content.ts +38 -0
- package/src/services/content/genre.ts +139 -0
- package/src/services/content/instructor.ts +131 -0
- package/src/services/sanity.js +105 -266
- package/.claude/settings.local.json +0 -8
package/src/services/sanity.js
CHANGED
|
@@ -22,19 +22,16 @@ import {
|
|
|
22
22
|
SONG_TYPES,
|
|
23
23
|
SONG_TYPES_WITH_CHILDREN,
|
|
24
24
|
} from '../contentTypeConfig.js'
|
|
25
|
-
import {fetchSimilarItems, recommendations} from './recommendations.js'
|
|
25
|
+
import { fetchSimilarItems, recommendations } from './recommendations.js'
|
|
26
26
|
import { processMetadata, typeWithSortOrder } from '../contentMetaData.js'
|
|
27
27
|
|
|
28
28
|
import { globalConfig } from './config.js'
|
|
29
29
|
|
|
30
|
-
import {
|
|
31
|
-
fetchNextContentDataForParent,
|
|
32
|
-
fetchHandler,
|
|
33
|
-
} from './railcontent.js'
|
|
30
|
+
import { fetchNextContentDataForParent, fetchHandler } from './railcontent.js'
|
|
34
31
|
import { arrayToStringRepresentation, FilterBuilder } from '../filterBuilder.js'
|
|
35
32
|
import { fetchUserPermissions } from './user/permissions.js'
|
|
36
33
|
import { getAllCompleted, getAllStarted, getAllStartedOrCompleted } from './contentProgress.js'
|
|
37
|
-
import {fetchRecentActivitiesActiveTabs} from
|
|
34
|
+
import { fetchRecentActivitiesActiveTabs } from './userActivity.js'
|
|
38
35
|
|
|
39
36
|
/**
|
|
40
37
|
* Exported functions that are excluded from index generation.
|
|
@@ -162,30 +159,6 @@ function getQueryFromPage(pageNumber, contentPerPage) {
|
|
|
162
159
|
return result
|
|
163
160
|
}
|
|
164
161
|
|
|
165
|
-
/**
|
|
166
|
-
* Fetch all artists with lessons available for a specific brand.
|
|
167
|
-
*
|
|
168
|
-
* @param {string} brand - The brand for which to fetch artists.
|
|
169
|
-
* @returns {Promise<Object|null>} - A promise that resolves to an array of artist objects or null if not found.
|
|
170
|
-
*
|
|
171
|
-
* @example
|
|
172
|
-
* fetchArtists('drumeo')
|
|
173
|
-
* .then(artists => console.log(artists))
|
|
174
|
-
* .catch(error => console.error(error));
|
|
175
|
-
*/
|
|
176
|
-
export async function fetchArtists(brand) {
|
|
177
|
-
const filter = await new FilterBuilder(
|
|
178
|
-
`_type == "song" && brand == "${brand}" && references(^._id)`,
|
|
179
|
-
{ bypassPermissions: true }
|
|
180
|
-
).buildFilter()
|
|
181
|
-
const query = `
|
|
182
|
-
*[_type == "artist"]{
|
|
183
|
-
name,
|
|
184
|
-
"lessonsCount": count(*[${filter}])
|
|
185
|
-
}[lessonsCount > 0] |order(lower(name)) `
|
|
186
|
-
return fetchSanity(query, true, { processNeedAccess: false })
|
|
187
|
-
}
|
|
188
|
-
|
|
189
162
|
/**
|
|
190
163
|
* Fetch current number of artists for songs within a brand.
|
|
191
164
|
* @param {string} brand - The current brand.
|
|
@@ -335,7 +308,7 @@ export async function fetchNewReleases(
|
|
|
335
308
|
web_url_path,
|
|
336
309
|
"permission_id": permission[]->railcontent_id,
|
|
337
310
|
`
|
|
338
|
-
const query = buildRawQuery(filter, fields, {sortOrder: sortOrder, start, end: end})
|
|
311
|
+
const query = buildRawQuery(filter, fields, { sortOrder: sortOrder, start, end: end })
|
|
339
312
|
return fetchSanity(query, true)
|
|
340
313
|
}
|
|
341
314
|
|
|
@@ -476,17 +449,26 @@ export async function fetchByRailContentId(id, contentType) {
|
|
|
476
449
|
* .then(contents => console.log(contents))
|
|
477
450
|
* .catch(error => console.error(error));
|
|
478
451
|
*/
|
|
479
|
-
export async function fetchByRailContentIds(
|
|
452
|
+
export async function fetchByRailContentIds(
|
|
453
|
+
ids,
|
|
454
|
+
contentType = undefined,
|
|
455
|
+
brand = undefined,
|
|
456
|
+
includePermissionsAndStatusFilter = false
|
|
457
|
+
) {
|
|
480
458
|
if (!ids?.length) {
|
|
481
459
|
return []
|
|
482
460
|
}
|
|
483
|
-
ids = [...new Set(ids.filter(item => item !== null && item !== undefined))]
|
|
461
|
+
ids = [...new Set(ids.filter((item) => item !== null && item !== undefined))]
|
|
484
462
|
const idsString = ids.join(',')
|
|
485
463
|
const brandFilter = brand ? ` && brand == "${brand}"` : ''
|
|
486
|
-
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {
|
|
464
|
+
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {
|
|
465
|
+
pullFutureContent: true,
|
|
466
|
+
}).buildFilter()
|
|
487
467
|
const fields = await getFieldsForContentTypeWithFilteredChildren(contentType, true)
|
|
488
468
|
const baseFilter = `railcontent_id in [${idsString}]${brandFilter}`
|
|
489
|
-
const finalFilter = includePermissionsAndStatusFilter
|
|
469
|
+
const finalFilter = includePermissionsAndStatusFilter
|
|
470
|
+
? await new FilterBuilder(baseFilter).buildFilter()
|
|
471
|
+
: baseFilter
|
|
490
472
|
const query = `*[
|
|
491
473
|
${finalFilter}
|
|
492
474
|
]{
|
|
@@ -498,18 +480,16 @@ export async function fetchByRailContentIds(ids, contentType = undefined, brand
|
|
|
498
480
|
|
|
499
481
|
console.log('ids query', query)
|
|
500
482
|
const customPostProcess = (results) => {
|
|
501
|
-
const now = getSanityDate(new Date(), false)
|
|
483
|
+
const now = getSanityDate(new Date(), false)
|
|
502
484
|
const liveProcess = (result) => {
|
|
503
485
|
if (result.live_event_start_time && result.live_event_end_time) {
|
|
504
|
-
result.isLive =
|
|
505
|
-
result.live_event_start_time <= now &&
|
|
506
|
-
result.live_event_end_time >= now;
|
|
486
|
+
result.isLive = result.live_event_start_time <= now && result.live_event_end_time >= now
|
|
507
487
|
} else {
|
|
508
|
-
result.isLive = false
|
|
488
|
+
result.isLive = false
|
|
509
489
|
}
|
|
510
|
-
return result
|
|
511
|
-
}
|
|
512
|
-
return results.map(liveProcess)
|
|
490
|
+
return result
|
|
491
|
+
}
|
|
492
|
+
return results.map(liveProcess)
|
|
513
493
|
}
|
|
514
494
|
const results = await fetchSanity(query, true, { customPostProcess: customPostProcess })
|
|
515
495
|
|
|
@@ -527,13 +507,14 @@ export async function fetchByRailContentIds(ids, contentType = undefined, brand
|
|
|
527
507
|
return sortedResults
|
|
528
508
|
}
|
|
529
509
|
|
|
530
|
-
export async function fetchContentRows(brand, pageName, contentRowSlug)
|
|
531
|
-
{
|
|
510
|
+
export async function fetchContentRows(brand, pageName, contentRowSlug) {
|
|
532
511
|
if (pageName === 'lessons') pageName = 'lesson'
|
|
533
512
|
if (pageName === 'songs') pageName = 'song'
|
|
534
513
|
const rowString = contentRowSlug ? ` && slug.current == "${contentRowSlug.toLowerCase()}"` : ''
|
|
535
|
-
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {
|
|
536
|
-
|
|
514
|
+
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {
|
|
515
|
+
pullFutureContent: true,
|
|
516
|
+
}).buildFilter()
|
|
517
|
+
const childFilter = await new FilterBuilder('', { isChildrenFilter: true }).buildFilter()
|
|
537
518
|
const query = `*[_type == 'recommended-content-row' && brand == '${brand}' && type == '${pageName}'${rowString}]{
|
|
538
519
|
brand,
|
|
539
520
|
name,
|
|
@@ -549,8 +530,6 @@ export async function fetchContentRows(brand, pageName, contentRowSlug)
|
|
|
549
530
|
return fetchSanity(query, true)
|
|
550
531
|
}
|
|
551
532
|
|
|
552
|
-
|
|
553
|
-
|
|
554
533
|
/**
|
|
555
534
|
* Fetch all content for a specific brand and type with pagination, search, and grouping options.
|
|
556
535
|
* @param {string} brand - The brand for which to fetch content.
|
|
@@ -619,9 +598,7 @@ export async function fetchAll(
|
|
|
619
598
|
} else if (type === 'pack') {
|
|
620
599
|
typeFilter = `&& (_type == 'pack' || _type == 'semester-pack')`
|
|
621
600
|
} else {
|
|
622
|
-
typeFilter = type
|
|
623
|
-
? `&& _type == '${type}'`
|
|
624
|
-
: ''
|
|
601
|
+
typeFilter = type ? `&& _type == '${type}'` : ''
|
|
625
602
|
}
|
|
626
603
|
|
|
627
604
|
// Construct the search filter
|
|
@@ -740,7 +717,7 @@ async function getProgressFilter(progress, progressIds) {
|
|
|
740
717
|
return `&& (railcontent_id in [${ids.join(',')}])`
|
|
741
718
|
}
|
|
742
719
|
case 'incomplete': {
|
|
743
|
-
const ids = progressIds !== undefined ? progressIds :await getAllStarted()
|
|
720
|
+
const ids = progressIds !== undefined ? progressIds : await getAllStarted()
|
|
744
721
|
return `&& railcontent_id in [${ids.join(',')}]`
|
|
745
722
|
}
|
|
746
723
|
default:
|
|
@@ -1248,7 +1225,7 @@ async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, cou
|
|
|
1248
1225
|
*[${filterSongTypesWithSameLicense}]->{${queryFields}}|order(published_on desc, title asc)[0...${count}],
|
|
1249
1226
|
}[0...1]`
|
|
1250
1227
|
const results = await fetchSanity(query, false)
|
|
1251
|
-
return results ? results['related_by_license'] ?? [] : []
|
|
1228
|
+
return results ? (results['related_by_license'] ?? []) : []
|
|
1252
1229
|
}
|
|
1253
1230
|
|
|
1254
1231
|
/**
|
|
@@ -1257,15 +1234,17 @@ async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, cou
|
|
|
1257
1234
|
* @param {string} brand - The current brand.
|
|
1258
1235
|
* @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
|
|
1259
1236
|
*/
|
|
1260
|
-
export async function fetchSiblingContent(railContentId, brand= null)
|
|
1261
|
-
{
|
|
1237
|
+
export async function fetchSiblingContent(railContentId, brand = null) {
|
|
1262
1238
|
const filterGetParent = await new FilterBuilder(`references(^._id) && _type == ^.parent_type`, {
|
|
1263
|
-
pullFutureContent: true
|
|
1264
|
-
}).buildFilter()
|
|
1265
|
-
const filterForParentList = await new FilterBuilder(`references(^._id) && _type == ^.parent_type`, {
|
|
1266
1239
|
pullFutureContent: true,
|
|
1267
|
-
isParentFilter: true,
|
|
1268
1240
|
}).buildFilter()
|
|
1241
|
+
const filterForParentList = await new FilterBuilder(
|
|
1242
|
+
`references(^._id) && _type == ^.parent_type`,
|
|
1243
|
+
{
|
|
1244
|
+
pullFutureContent: true,
|
|
1245
|
+
isParentFilter: true,
|
|
1246
|
+
}
|
|
1247
|
+
).buildFilter()
|
|
1269
1248
|
|
|
1270
1249
|
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
1271
1250
|
|
|
@@ -1305,8 +1284,7 @@ export async function fetchSiblingContent(railContentId, brand= null)
|
|
|
1305
1284
|
* @param {string} railContentId - The RailContent ID of the current lesson.
|
|
1306
1285
|
* @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
|
|
1307
1286
|
*/
|
|
1308
|
-
export async function fetchRelatedLessons(railContentId)
|
|
1309
|
-
{
|
|
1287
|
+
export async function fetchRelatedLessons(railContentId) {
|
|
1310
1288
|
const defaultFilterFields = `_type==^._type && brand == ^.brand && railcontent_id != ${railContentId}`
|
|
1311
1289
|
|
|
1312
1290
|
const filterSameArtist = await new FilterBuilder(
|
|
@@ -1350,11 +1328,16 @@ export async function fetchAllPacks(
|
|
|
1350
1328
|
const start = (page - 1) * limit
|
|
1351
1329
|
const end = start + limit
|
|
1352
1330
|
|
|
1353
|
-
const query = await buildQuery(
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1331
|
+
const query = await buildQuery(
|
|
1332
|
+
filter,
|
|
1333
|
+
filterParams,
|
|
1334
|
+
await getFieldsForContentTypeWithFilteredChildren('pack'),
|
|
1335
|
+
{
|
|
1336
|
+
sortOrder: sortOrder,
|
|
1337
|
+
start,
|
|
1338
|
+
end,
|
|
1339
|
+
}
|
|
1340
|
+
)
|
|
1358
1341
|
return fetchSanity(query, true)
|
|
1359
1342
|
}
|
|
1360
1343
|
|
|
@@ -1460,45 +1443,6 @@ export async function fetchPackData(id) {
|
|
|
1460
1443
|
return fetchSanity(query, false)
|
|
1461
1444
|
}
|
|
1462
1445
|
|
|
1463
|
-
/**
|
|
1464
|
-
* Fetch the data needed for the coach screen.
|
|
1465
|
-
* @param {string} brand - The brand for which to fetch coach lessons
|
|
1466
|
-
* @param {string} id - The Railcontent ID of the coach
|
|
1467
|
-
* @returns {Promise<Object|null>} - The lessons for the instructor or null if not found.
|
|
1468
|
-
* @param {Object} params - Parameters for pagination, filtering and sorting.
|
|
1469
|
-
* @param {string} [params.sortOrder="-published_on"] - The field to sort the lessons by.
|
|
1470
|
-
* @param {string} [params.searchTerm=""] - The search term to filter content by title.
|
|
1471
|
-
* @param {number} [params.page=1] - The page number for pagination.
|
|
1472
|
-
* @param {number} [params.limit=10] - The number of items per page.
|
|
1473
|
-
* @param {Array<string>} [params.includedFields=[]] - Additional filters to apply to the query in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock'].
|
|
1474
|
-
*
|
|
1475
|
-
* @example
|
|
1476
|
-
* fetchCoachLessons('coach123')
|
|
1477
|
-
* .then(lessons => console.log(lessons))
|
|
1478
|
-
* .catch(error => console.error(error));
|
|
1479
|
-
*/
|
|
1480
|
-
export async function fetchCoachLessons(
|
|
1481
|
-
brand,
|
|
1482
|
-
id,
|
|
1483
|
-
{ sortOrder = '-published_on', searchTerm = '', page = 1, limit = 20, includedFields = [] } = {}
|
|
1484
|
-
) {
|
|
1485
|
-
const fieldsString = getFieldsForContentType()
|
|
1486
|
-
const start = (page - 1) * limit
|
|
1487
|
-
const end = start + limit
|
|
1488
|
-
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1489
|
-
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
1490
|
-
const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} && references(*[_type=='instructor' && railcontent_id == ${id}]._id)`
|
|
1491
|
-
const filterWithRestrictions = await new FilterBuilder(filter).buildFilter()
|
|
1492
|
-
|
|
1493
|
-
sortOrder = getSortOrder(sortOrder, brand)
|
|
1494
|
-
const query = buildEntityAndTotalQuery(filterWithRestrictions, fieldsString, {
|
|
1495
|
-
sortOrder: sortOrder,
|
|
1496
|
-
start: start,
|
|
1497
|
-
end: end,
|
|
1498
|
-
})
|
|
1499
|
-
return fetchSanity(query, true)
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
1446
|
/**
|
|
1503
1447
|
* Fetch the data needed for the coach screen.
|
|
1504
1448
|
* @param {string} id - The Railcontent ID of the coach
|
|
@@ -1530,121 +1474,6 @@ export async function fetchByReference(
|
|
|
1530
1474
|
return fetchSanity(query, true)
|
|
1531
1475
|
}
|
|
1532
1476
|
|
|
1533
|
-
/**
|
|
1534
|
-
* Fetch the artist's lessons.
|
|
1535
|
-
* @param {string} brand - The brand for which to fetch lessons.
|
|
1536
|
-
* @param {string} name - The name of the artist
|
|
1537
|
-
* @param {string} contentType - The type of the lessons we need to get from the artist. If not defined, groq will get lessons from all content types
|
|
1538
|
-
* @param {Object} params - Parameters for sorting, searching, pagination and filtering.
|
|
1539
|
-
* @param {string} [params.sort="-published_on"] - The field to sort the lessons by.
|
|
1540
|
-
* @param {string} [params.searchTerm=""] - The search term to filter the lessons.
|
|
1541
|
-
* @param {number} [params.page=1] - The page number for pagination.
|
|
1542
|
-
* @param {number} [params.limit=10] - The number of items per page.
|
|
1543
|
-
* @param {Array<string>} [params.includedFields=[]] - Additional filters to apply to the query in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock'].
|
|
1544
|
-
* @param {Array<number>} [params.progressIds] - The ids of the lessons that are in progress or completed
|
|
1545
|
-
* @returns {Promise<Object|null>} - The lessons for the artist and some details about the artist (name and thumbnail).
|
|
1546
|
-
*
|
|
1547
|
-
* @example
|
|
1548
|
-
* fetchArtistLessons('drumeo', '10 Years', 'song', {'-published_on', '', 1, 10, ["difficulty,Intermediate"], [232168, 232824, 303375, 232194, 393125]})
|
|
1549
|
-
* .then(lessons => console.log(lessons))
|
|
1550
|
-
* .catch(error => console.error(error));
|
|
1551
|
-
*/
|
|
1552
|
-
export async function fetchArtistLessons(
|
|
1553
|
-
brand,
|
|
1554
|
-
name,
|
|
1555
|
-
contentType,
|
|
1556
|
-
{
|
|
1557
|
-
sort = '-published_on',
|
|
1558
|
-
searchTerm = '',
|
|
1559
|
-
page = 1,
|
|
1560
|
-
limit = 10,
|
|
1561
|
-
includedFields = [],
|
|
1562
|
-
progressIds = undefined,
|
|
1563
|
-
} = {}
|
|
1564
|
-
) {
|
|
1565
|
-
const fieldsString = DEFAULT_FIELDS.join(',')
|
|
1566
|
-
const start = (page - 1) * limit
|
|
1567
|
-
const end = start + limit
|
|
1568
|
-
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1569
|
-
const sortOrder = getSortOrder(sort, brand)
|
|
1570
|
-
const addType =
|
|
1571
|
-
contentType && Array.isArray(contentType)
|
|
1572
|
-
? `_type in ['${contentType.join("', '")}'] &&`
|
|
1573
|
-
: contentType
|
|
1574
|
-
? `_type == '${contentType}' && `
|
|
1575
|
-
: ''
|
|
1576
|
-
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
1577
|
-
|
|
1578
|
-
// limits the results to supplied progressIds for started & completed filters
|
|
1579
|
-
const progressFilter =
|
|
1580
|
-
progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
1581
|
-
const now = getSanityDate(new Date())
|
|
1582
|
-
const query = `{
|
|
1583
|
-
"entity":
|
|
1584
|
-
*[_type == 'artist' && name == '${name}']
|
|
1585
|
-
{'type': _type, name, 'thumbnail':thumbnail_url.asset->url,
|
|
1586
|
-
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1587
|
-
'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
|
|
1588
|
-
[${start}...${end}]}
|
|
1589
|
-
|order(${sortOrder})f
|
|
1590
|
-
}`
|
|
1591
|
-
return fetchSanity(query, true)
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
/**
|
|
1595
|
-
* Fetch the genre's lessons.
|
|
1596
|
-
* @param {string} brand - The brand for which to fetch lessons.
|
|
1597
|
-
* @param {string} name - The name of the genre
|
|
1598
|
-
* @param {Object} params - Parameters for sorting, searching, pagination and filtering.
|
|
1599
|
-
* @param {string} [params.sort="-published_on"] - The field to sort the lessons by.
|
|
1600
|
-
* @param {string} [params.searchTerm=""] - The search term to filter the lessons.
|
|
1601
|
-
* @param {number} [params.page=1] - The page number for pagination.
|
|
1602
|
-
* @param {number} [params.limit=10] - The number of items per page.
|
|
1603
|
-
* @param {Array<string>} [params.includedFields=[]] - Additional filters to apply to the query in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock'].
|
|
1604
|
-
* @param {Array<number>} [params.progressIds] - The ids of the lessons that are in progress or completed
|
|
1605
|
-
* @returns {Promise<Object|null>} - The lessons for the artist and some details about the artist (name and thumbnail).
|
|
1606
|
-
*
|
|
1607
|
-
* @example
|
|
1608
|
-
* fetchGenreLessons('drumeo', 'Blues', 'song', {'-published_on', '', 1, 10, ["difficulty,Intermediate"], [232168, 232824, 303375, 232194, 393125]})
|
|
1609
|
-
* .then(lessons => console.log(lessons))
|
|
1610
|
-
* .catch(error => console.error(error));
|
|
1611
|
-
*/
|
|
1612
|
-
export async function fetchGenreLessons(
|
|
1613
|
-
brand,
|
|
1614
|
-
name,
|
|
1615
|
-
contentType,
|
|
1616
|
-
{
|
|
1617
|
-
sort = '-published_on',
|
|
1618
|
-
searchTerm = '',
|
|
1619
|
-
page = 1,
|
|
1620
|
-
limit = 10,
|
|
1621
|
-
includedFields = [],
|
|
1622
|
-
progressIds = undefined,
|
|
1623
|
-
} = {}
|
|
1624
|
-
) {
|
|
1625
|
-
const fieldsString = DEFAULT_FIELDS.join(',')
|
|
1626
|
-
const start = (page - 1) * limit
|
|
1627
|
-
const end = start + limit
|
|
1628
|
-
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
|
|
1629
|
-
const sortOrder = getSortOrder(sort, brand)
|
|
1630
|
-
const addType = contentType ? `_type == '${contentType}' && ` : ''
|
|
1631
|
-
const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
|
|
1632
|
-
// limits the results to supplied progressIds for started & completed filters
|
|
1633
|
-
const progressFilter =
|
|
1634
|
-
progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
|
|
1635
|
-
const now = getSanityDate(new Date())
|
|
1636
|
-
const query = `{
|
|
1637
|
-
"entity":
|
|
1638
|
-
*[_type == 'genre' && name == '${name}']
|
|
1639
|
-
{'type': _type, name, 'thumbnail':thumbnail_url.asset->url,
|
|
1640
|
-
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1641
|
-
'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
|
|
1642
|
-
[${start}...${end}]}
|
|
1643
|
-
|order(${sortOrder})
|
|
1644
|
-
}`
|
|
1645
|
-
return fetchSanity(query, true)
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
1477
|
export async function fetchTopLevelParentId(railcontentId) {
|
|
1649
1478
|
const statusFilter = "&& status in ['scheduled', 'published', 'archived', 'unlisted']"
|
|
1650
1479
|
|
|
@@ -1766,8 +1595,10 @@ export async function fetchCommentModContentData(ids) {
|
|
|
1766
1595
|
*
|
|
1767
1596
|
* @param {string} query - The GROQ query to execute against the Sanity API.
|
|
1768
1597
|
* @param {boolean} isList - Whether to return an array or a single result.
|
|
1769
|
-
* @param {
|
|
1770
|
-
* @param {
|
|
1598
|
+
* @param {Object} options - Additional options for fetching data.
|
|
1599
|
+
* @param {Function} [options.customPostProcess=null] - custom post process callback
|
|
1600
|
+
* @param {boolean} [options.processNeedAccess=true] - execute the needs_access callback
|
|
1601
|
+
* @param {boolean} [options.processPageType=true] - execute the page_type callback
|
|
1771
1602
|
* @returns {Promise<*|null>} - A promise that resolves to the fetched data or null if an error occurs or no results are found.
|
|
1772
1603
|
*
|
|
1773
1604
|
* @example
|
|
@@ -1780,7 +1611,7 @@ export async function fetchCommentModContentData(ids) {
|
|
|
1780
1611
|
export async function fetchSanity(
|
|
1781
1612
|
query,
|
|
1782
1613
|
isList,
|
|
1783
|
-
{ customPostProcess = null, processNeedAccess = true } = {}
|
|
1614
|
+
{ customPostProcess = null, processNeedAccess = true, processPageType = true } = {}
|
|
1784
1615
|
) {
|
|
1785
1616
|
// Check the config object before proceeding
|
|
1786
1617
|
if (!checkSanityConfig(globalConfig)) {
|
|
@@ -1822,19 +1653,20 @@ export async function fetchSanity(
|
|
|
1822
1653
|
results = processNeedAccess
|
|
1823
1654
|
? await needsAccessDecorator(results, userPermissions, isAdmin)
|
|
1824
1655
|
: results
|
|
1825
|
-
results =
|
|
1656
|
+
results = processPageType
|
|
1657
|
+
? pageTypeDecorator(results)
|
|
1658
|
+
: results
|
|
1826
1659
|
return customPostProcess ? customPostProcess(results) : results
|
|
1827
1660
|
} else {
|
|
1828
1661
|
throw new Error('No results found')
|
|
1829
1662
|
}
|
|
1830
1663
|
} catch (error) {
|
|
1831
|
-
console.error('fetchSanity: Fetch error:', {error, query})
|
|
1664
|
+
console.error('fetchSanity: Fetch error:', { error, query })
|
|
1832
1665
|
return null
|
|
1833
1666
|
}
|
|
1834
1667
|
}
|
|
1835
1668
|
|
|
1836
|
-
function contentResultsDecorator(results, fieldName, callback)
|
|
1837
|
-
{
|
|
1669
|
+
function contentResultsDecorator(results, fieldName, callback) {
|
|
1838
1670
|
if (Array.isArray(results)) {
|
|
1839
1671
|
results.forEach((result) => {
|
|
1840
1672
|
result[fieldName] = callback(result)
|
|
@@ -1861,16 +1693,18 @@ function contentResultsDecorator(results, fieldName, callback)
|
|
|
1861
1693
|
return results
|
|
1862
1694
|
}
|
|
1863
1695
|
|
|
1864
|
-
function pageTypeDecorator(results)
|
|
1865
|
-
{
|
|
1866
|
-
|
|
1696
|
+
function pageTypeDecorator(results) {
|
|
1697
|
+
return contentResultsDecorator(results, 'page_type', function (content) {
|
|
1698
|
+
return SONG_TYPES_WITH_CHILDREN.includes(content['type']) ? 'song' : 'lesson'
|
|
1699
|
+
})
|
|
1867
1700
|
}
|
|
1868
1701
|
|
|
1869
|
-
|
|
1870
1702
|
function needsAccessDecorator(results, userPermissions, isAdmin) {
|
|
1871
1703
|
if (globalConfig.sanityConfig.useDummyRailContentMethods) return results
|
|
1872
1704
|
userPermissions = new Set(userPermissions)
|
|
1873
|
-
return contentResultsDecorator(results, 'need_access', function (content) {
|
|
1705
|
+
return contentResultsDecorator(results, 'need_access', function (content) {
|
|
1706
|
+
return doesUserNeedAccessToContent(content, userPermissions, isAdmin)
|
|
1707
|
+
})
|
|
1874
1708
|
}
|
|
1875
1709
|
|
|
1876
1710
|
function doesUserNeedAccessToContent(result, userPermissions, isAdmin) {
|
|
@@ -1928,8 +1762,8 @@ export async function fetchShowsData(brand) {
|
|
|
1928
1762
|
* .catch(error => console.error(error));
|
|
1929
1763
|
*/
|
|
1930
1764
|
export async function fetchMetadata(brand, type) {
|
|
1931
|
-
let processedData =
|
|
1932
|
-
if(processedData?.onlyAvailableTabs === true) {
|
|
1765
|
+
let processedData = processMetadata(brand, type, true)
|
|
1766
|
+
if (processedData?.onlyAvailableTabs === true) {
|
|
1933
1767
|
const activeTabs = await fetchRecentActivitiesActiveTabs()
|
|
1934
1768
|
processedData.tabs = activeTabs
|
|
1935
1769
|
}
|
|
@@ -1955,7 +1789,7 @@ function arrayJoinWithQuotes(array, delimiter = ',') {
|
|
|
1955
1789
|
return wrapped.join(delimiter)
|
|
1956
1790
|
}
|
|
1957
1791
|
|
|
1958
|
-
function getSanityDate(date, roundToHourForCaching = true) {
|
|
1792
|
+
export function getSanityDate(date, roundToHourForCaching = true) {
|
|
1959
1793
|
if (roundToHourForCaching) {
|
|
1960
1794
|
// We need to set the published on filter date to be a round time so that it doesn't bypass the query cache
|
|
1961
1795
|
// with every request by changing the filter date every second. I've set it to one minute past the current hour
|
|
@@ -2029,13 +1863,19 @@ async function buildQuery(
|
|
|
2029
1863
|
return buildRawQuery(filter, fields, { sortOrder, start, end, isSingle })
|
|
2030
1864
|
}
|
|
2031
1865
|
|
|
2032
|
-
function buildEntityAndTotalQuery(
|
|
1866
|
+
export function buildEntityAndTotalQuery(
|
|
2033
1867
|
filter = '',
|
|
2034
1868
|
fields = '...',
|
|
2035
|
-
{
|
|
1869
|
+
{
|
|
1870
|
+
sortOrder = 'published_on desc',
|
|
1871
|
+
start = 0,
|
|
1872
|
+
end = 10,
|
|
1873
|
+
isSingle = false,
|
|
1874
|
+
withoutPagination = false,
|
|
1875
|
+
}
|
|
2036
1876
|
) {
|
|
2037
1877
|
const sortString = sortOrder ? ` | order(${sortOrder})` : ''
|
|
2038
|
-
const countString = isSingle ? '[0...1]' :
|
|
1878
|
+
const countString = isSingle ? '[0...1]' : withoutPagination ? `` : `[${start}...${end}]`
|
|
2039
1879
|
const query = `{
|
|
2040
1880
|
"entity": *[${filter}] ${sortString}${countString}
|
|
2041
1881
|
{
|
|
@@ -2164,22 +2004,22 @@ export async function fetchTabData(
|
|
|
2164
2004
|
|
|
2165
2005
|
switch (progress) {
|
|
2166
2006
|
case 'recent':
|
|
2167
|
-
progressIds = await getAllStartedOrCompleted({ brand, onlyIds: true })
|
|
2168
|
-
sortOrder = null
|
|
2169
|
-
break
|
|
2007
|
+
progressIds = await getAllStartedOrCompleted({ brand, onlyIds: true })
|
|
2008
|
+
sortOrder = null
|
|
2009
|
+
break
|
|
2170
2010
|
case 'incomplete':
|
|
2171
|
-
progressIds = await getAllStarted()
|
|
2172
|
-
sortOrder = null
|
|
2173
|
-
break
|
|
2011
|
+
progressIds = await getAllStarted()
|
|
2012
|
+
sortOrder = null
|
|
2013
|
+
break
|
|
2174
2014
|
case 'completed':
|
|
2175
|
-
progressIds = await getAllCompleted()
|
|
2176
|
-
sortOrder = null
|
|
2177
|
-
break
|
|
2015
|
+
progressIds = await getAllCompleted()
|
|
2016
|
+
sortOrder = null
|
|
2017
|
+
break
|
|
2178
2018
|
}
|
|
2179
2019
|
|
|
2180
2020
|
// limits the results to supplied progressIds for started & completed filters
|
|
2181
2021
|
const progressFilter = await getProgressFilter(progress, progressIds)
|
|
2182
|
-
const fieldsString = getFieldsForContentType('tab-data')
|
|
2022
|
+
const fieldsString = getFieldsForContentType('tab-data')
|
|
2183
2023
|
const now = getSanityDate(new Date())
|
|
2184
2024
|
|
|
2185
2025
|
// Determine the group by clause
|
|
@@ -2191,8 +2031,7 @@ export async function fetchTabData(
|
|
|
2191
2031
|
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
2192
2032
|
const childrenFields = await getChildFieldsForContentType('tab-data')
|
|
2193
2033
|
const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`).buildFilter()
|
|
2194
|
-
entityFieldsString =
|
|
2195
|
-
` ${fieldsString}
|
|
2034
|
+
entityFieldsString = ` ${fieldsString}
|
|
2196
2035
|
'children': child[${childrenFilter}]->{ ${childrenFields} 'children': child[${childrenFilter}]->{ ${childrenFields} }, },
|
|
2197
2036
|
'isLive': live_event_start_time <= "${now}" && live_event_end_time >= "${now}",
|
|
2198
2037
|
'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
|
|
@@ -2208,23 +2047,23 @@ export async function fetchTabData(
|
|
|
2208
2047
|
query = buildEntityAndTotalQuery(filterWithRestrictions, entityFieldsString, {
|
|
2209
2048
|
sortOrder: sortOrder,
|
|
2210
2049
|
start: start,
|
|
2211
|
-
end: end
|
|
2050
|
+
end: end,
|
|
2212
2051
|
})
|
|
2213
2052
|
|
|
2214
|
-
let results = await fetchSanity(query, true)
|
|
2053
|
+
let results = await fetchSanity(query, true)
|
|
2215
2054
|
|
|
2216
2055
|
if (['recent', 'incomplete', 'completed'].includes(progress) && results.entity.length > 1) {
|
|
2217
2056
|
const orderMap = new Map(progressIds.map((id, index) => [id, index]))
|
|
2218
2057
|
results.entity = results.entity
|
|
2219
2058
|
.sort((a, b) => {
|
|
2220
|
-
const aIdx = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER
|
|
2221
|
-
const bIdx = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER
|
|
2222
|
-
return aIdx - bIdx || new Date(b.published_on) - new Date(a.published_on)
|
|
2059
|
+
const aIdx = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER
|
|
2060
|
+
const bIdx = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER
|
|
2061
|
+
return aIdx - bIdx || new Date(b.published_on) - new Date(a.published_on)
|
|
2223
2062
|
})
|
|
2224
|
-
.slice(start, end)
|
|
2063
|
+
.slice(start, end)
|
|
2225
2064
|
}
|
|
2226
2065
|
|
|
2227
|
-
return results
|
|
2066
|
+
return results
|
|
2228
2067
|
}
|
|
2229
2068
|
|
|
2230
2069
|
export async function fetchRecent(
|
|
@@ -2299,12 +2138,12 @@ export async function fetchShows(brand, type, sort = 'sort') {
|
|
|
2299
2138
|
* @returns {Promise<*|null>}
|
|
2300
2139
|
*/
|
|
2301
2140
|
export async function fetchMethodV2IntroVideo(brand) {
|
|
2302
|
-
const type =
|
|
2303
|
-
const filter = `_type == '${type}' && brand == '${brand}'
|
|
2304
|
-
const fields = getIntroVideoFields('method-v2')
|
|
2141
|
+
const type = 'method-intro'
|
|
2142
|
+
const filter = `_type == '${type}' && brand == '${brand}'`
|
|
2143
|
+
const fields = getIntroVideoFields('method-v2')
|
|
2305
2144
|
|
|
2306
|
-
const query = `*[${filter}] { ${fields.join(
|
|
2307
|
-
return fetchSanity(query, false)
|
|
2145
|
+
const query = `*[${filter}] { ${fields.join(', ')} }`
|
|
2146
|
+
return fetchSanity(query, false)
|
|
2308
2147
|
}
|
|
2309
2148
|
|
|
2310
2149
|
/**
|
|
@@ -2313,13 +2152,13 @@ export async function fetchMethodV2IntroVideo(brand) {
|
|
|
2313
2152
|
* @returns {Promise<*|null>}
|
|
2314
2153
|
*/
|
|
2315
2154
|
export async function fetchMethodV2Structure(brand) {
|
|
2316
|
-
const _type =
|
|
2155
|
+
const _type = 'method-v2'
|
|
2317
2156
|
const query = `*[_type == '${_type}' && brand == '${brand}'][0...1]{
|
|
2318
2157
|
'sanity_id': _id,
|
|
2319
2158
|
'learningPaths': child[]->{
|
|
2320
2159
|
'id': railcontent_id,
|
|
2321
2160
|
'children': child[]->railcontent_id
|
|
2322
2161
|
}
|
|
2323
|
-
}
|
|
2324
|
-
return await fetchSanity(query, false)
|
|
2162
|
+
}`
|
|
2163
|
+
return await fetchSanity(query, false)
|
|
2325
2164
|
}
|