musora-content-services 2.10.0 → 2.11.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.
Files changed (164) hide show
  1. package/.coderabbit.yaml +0 -0
  2. package/.editorconfig +0 -0
  3. package/.github/pull_request_template.md +0 -0
  4. package/.github/workflows/node.js.yml +0 -0
  5. package/.prettierignore +0 -0
  6. package/.prettierrc +0 -0
  7. package/CHANGELOG.md +17 -0
  8. package/README.md +1 -2
  9. package/babel.config.cjs +0 -0
  10. package/docs/Content-Organization.html +0 -0
  11. package/docs/ContentOrganization.html +0 -0
  12. package/docs/Gamification.html +0 -0
  13. package/docs/UserManagement.html +0 -0
  14. package/docs/UserManagementSystem.html +0 -0
  15. package/docs/api_types.js.html +0 -0
  16. package/docs/config.js.html +0 -0
  17. package/docs/content-org_content-org.js.html +0 -0
  18. package/docs/content-org_playlists-types.js.html +0 -0
  19. package/docs/content-org_playlists.js.html +0 -0
  20. package/docs/content.js.html +0 -0
  21. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  26. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  27. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  28. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  36. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  37. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  39. package/docs/gamification_awards.js.html +0 -0
  40. package/docs/gamification_gamification.js.html +0 -0
  41. package/docs/gamification_types.js.html +0 -0
  42. package/docs/global.html +0 -0
  43. package/docs/global.html#User +0 -0
  44. package/docs/index.html +0 -0
  45. package/docs/module-Awards.html +0 -0
  46. package/docs/module-Config.html +0 -0
  47. package/docs/module-Content-Services-V2.html +0 -0
  48. package/docs/module-Interests.html +0 -0
  49. package/docs/module-Notifications.html +0 -0
  50. package/docs/module-Permissions.html +0 -0
  51. package/docs/module-Playlists.html +0 -0
  52. package/docs/module-Railcontent-Services.html +0 -0
  53. package/docs/module-Sanity-Services.html +0 -0
  54. package/docs/module-Session-Management.html +0 -0
  55. package/docs/module-Sessions.html +0 -0
  56. package/docs/module-User-Activity.html +0 -0
  57. package/docs/module-User-Management.html +0 -0
  58. package/docs/module-User-Permissions.html +0 -0
  59. package/docs/module-UserActivity.html +0 -0
  60. package/docs/module-UserManagement.html +0 -0
  61. package/docs/module-UserNotifications.html +0 -0
  62. package/docs/module-UserProfile.html +0 -0
  63. package/docs/railcontent.js.html +0 -0
  64. package/docs/sanity.js.html +0 -0
  65. package/docs/scripts/collapse.js +0 -0
  66. package/docs/scripts/commonNav.js +0 -0
  67. package/docs/scripts/linenumber.js +0 -0
  68. package/docs/scripts/nav.js +0 -0
  69. package/docs/scripts/polyfill.js +0 -0
  70. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  71. package/docs/scripts/prettify/lang-css.js +0 -0
  72. package/docs/scripts/prettify/prettify.js +0 -0
  73. package/docs/scripts/search.js +0 -0
  74. package/docs/styles/jsdoc.css +0 -0
  75. package/docs/styles/prettify.css +0 -0
  76. package/docs/types.js.html +0 -0
  77. package/docs/userActivity.js.html +0 -0
  78. package/docs/user_interests.js.html +0 -0
  79. package/docs/user_management.js.html +0 -0
  80. package/docs/user_notifications.js.html +0 -0
  81. package/docs/user_permissions.js.html +0 -0
  82. package/docs/user_profile.js.html +0 -0
  83. package/docs/user_sessions.js.html +0 -0
  84. package/docs/user_types.js.html +0 -0
  85. package/docs/user_user-management-system.js.html +0 -0
  86. package/docs/user_user-management.js.html +0 -0
  87. package/jest.config.js +0 -0
  88. package/jsdoc.json +0 -0
  89. package/link_mcs.sh +0 -0
  90. package/package.json +1 -1
  91. package/src/contentMetaData.js +0 -0
  92. package/src/contentTypeConfig.js +1 -1
  93. package/src/filterBuilder.js +0 -0
  94. package/src/index.d.ts +0 -0
  95. package/src/index.js +0 -0
  96. package/src/infrastructure/http/HttpClient.ts +0 -0
  97. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  98. package/src/infrastructure/http/index.ts +0 -0
  99. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  100. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  101. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  102. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  103. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  104. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  105. package/src/lib/httpHelper.js +0 -0
  106. package/src/lib/lastUpdated.js +0 -0
  107. package/src/services/api/types.js +0 -0
  108. package/src/services/config.js +0 -0
  109. package/src/services/content-org/content-org.js +0 -0
  110. package/src/services/content-org/playlists-types.js +0 -0
  111. package/src/services/content-org/playlists.js +0 -0
  112. package/src/services/content.js +0 -0
  113. package/src/services/contentAggregator.js +0 -0
  114. package/src/services/contentLikes.js +0 -0
  115. package/src/services/contentProgress.js +0 -0
  116. package/src/services/dataContext.js +0 -0
  117. package/src/services/dateUtils.js +0 -0
  118. package/src/services/forum.js +0 -0
  119. package/src/services/gamification/awards.js +0 -0
  120. package/src/services/gamification/gamification.js +0 -0
  121. package/src/services/gamification/types.js +0 -0
  122. package/src/services/imageSRCVerify.js +0 -0
  123. package/src/services/railcontent.js +4 -1
  124. package/src/services/recommendations.js +0 -0
  125. package/src/services/sanity.js +33 -21
  126. package/src/services/types.js +0 -0
  127. package/src/services/user/chat.js +0 -0
  128. package/src/services/user/interests.js +0 -0
  129. package/src/services/user/management.js +0 -0
  130. package/src/services/user/notifications.js +0 -0
  131. package/src/services/user/permissions.js +0 -0
  132. package/src/services/user/profile.js +0 -0
  133. package/src/services/user/sessions.js +0 -0
  134. package/src/services/user/types.js +0 -0
  135. package/src/services/user/user-management-system.js +0 -0
  136. package/src/services/userActivity.js +24 -14
  137. package/test/HttpClient.test.js +0 -0
  138. package/test/content.test.js +0 -0
  139. package/test/contentLikes.test.js +0 -0
  140. package/test/contentProgress.test.js +0 -0
  141. package/test/dataContext.test.js +0 -0
  142. package/test/forum.test.js +0 -0
  143. package/test/imageSRCBuilder.test.js +0 -0
  144. package/test/imageSRCVerify.test.js +0 -0
  145. package/test/initializeTests.js +0 -0
  146. package/test/lib/lastUpdated.test.js +0 -0
  147. package/test/live/contentProgressLive.test.js +0 -0
  148. package/test/live/railcontentLive.test.js +0 -0
  149. package/test/localStorageMock.js +0 -0
  150. package/test/log.js +0 -0
  151. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  152. package/test/mockData/mockData_progress_content.json +0 -0
  153. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  154. package/test/mockData/mockData_user_practices.json +0 -0
  155. package/test/progressRows.test.js +0 -0
  156. package/test/sanityQueryService.test.js +0 -0
  157. package/test/streakMessage.test.js +0 -0
  158. package/test/user/permissions.test.js +0 -0
  159. package/test/userActivity.test.js +0 -0
  160. package/tools/generate-index.cjs +0 -0
  161. package/.yarnrc.yml +0 -1
  162. package/docs/module-Content-Services.html +0 -763
  163. package/docs/module-UserChat.html +0 -410
  164. package/docs/user_chat.js.html +0 -98
package/.coderabbit.yaml CHANGED
File without changes
package/.editorconfig CHANGED
File without changes
File without changes
File without changes
package/.prettierignore CHANGED
File without changes
package/.prettierrc CHANGED
File without changes
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
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.11.1](https://github.com/railroadmedia/musora-content-services/compare/v2.11.0...v2.11.1) (2025-06-24)
6
+
7
+ ## [2.11.0](https://github.com/railroadmedia/musora-content-services/compare/v2.10.0...v2.11.0) (2025-06-24)
8
+
9
+
10
+ ### Features
11
+
12
+ * **MU2-698:** change extra minutes from 15 to 30 ([ccb1a96](https://github.com/railroadmedia/musora-content-services/commit/ccb1a9682c0d75a61743645434fb1ae5398ca509))
13
+ * **MU2-712:** Add live_event_start_time and live_event_end_time on fetchUpcomingEvents ([0de3f8e](https://github.com/railroadmedia/musora-content-services/commit/0de3f8ea68a2a8d29d122f6adf2d893669d3bd25))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * Duplicate practice per day in cache ([d44e0f6](https://github.com/railroadmedia/musora-content-services/commit/d44e0f6a384e78b5d515bcafc99cba30b24a535d))
19
+ * **MU2-693:** Fix cache(data versioning) on user practices ([ddc0d3b](https://github.com/railroadmedia/musora-content-services/commit/ddc0d3b3bd180c0f51241112ce7ba2094ff71c8c))
20
+ * User practice cache generation after one practice is restored ([dfb9e27](https://github.com/railroadmedia/musora-content-services/commit/dfb9e271e9efcb18b1d866935e0eb4acb7317b4b))
21
+
5
22
  ## [2.10.0](https://github.com/railroadmedia/musora-content-services/compare/v2.9.5...v2.10.0) (2025-06-23)
6
23
 
7
24
 
package/README.md CHANGED
@@ -6,7 +6,6 @@ allowing you to easily retrieve, filter, and manipulate content for use in vario
6
6
 
7
7
  ## Setup
8
8
  To set up the Musora Content Services project for local development, follow these steps:
9
- - Pull the latest railenvironment `php-8-3-upgrade` branch changes
10
9
  - In the railenvironment directory, start up the container with the `./rrr.sh` command
11
10
  - Run `r setup musora-content-services`
12
11
  - Run `npm install --save-dev jest`
@@ -94,4 +93,4 @@ and having jest installed (`npm install --save-dev jest`). To run the full test
94
93
  npm test
95
94
  ```
96
95
  You can also filter down to specific tests with the `-- -t="..."` option. e.g. you can run the userContext test suite
97
- with `npm test -- -t="userContext"` or just the contentLiked test with `npm test -- -t="contentLiked"`
96
+ with `npm test -- -t="userContext"` or just the contentLiked test with `npm test -- -t="contentLiked"`
package/babel.config.cjs 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
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
package/docs/global.html CHANGED
File without changes
File without changes
package/docs/index.html 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
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
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
package/jest.config.js CHANGED
File without changes
package/jsdoc.json CHANGED
File without changes
package/link_mcs.sh CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
@@ -416,7 +416,7 @@ export let contentTypeConfig = {
416
416
  '"lesson_count": coalesce(count(child[]->.child[]->), 0)',
417
417
  'xp',
418
418
  `"description": ${descriptionField}`,
419
- '"instructors": instructor[]->name',
419
+ '"instructors": instructor[]->{ "id": railcontent_id, name, "thumbnail_url": thumbnail_url.asset->url }',
420
420
  '"logo_image_url": logo_image_url.asset->url',
421
421
  'total_xp',
422
422
  `"children": child[]->{
File without changes
package/src/index.d.ts CHANGED
File without changes
package/src/index.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
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
@@ -755,7 +755,7 @@ export async function reportComment(commentId, issue) {
755
755
  return await postDataHandler(url, data)
756
756
  }
757
757
 
758
- export async function fetchUserPractices({ currentVersion, userId } = {}) {
758
+ export async function fetchUserPractices(currentVersion = 0, { userId } = {}) {
759
759
  const params = new URLSearchParams();
760
760
  if (userId) params.append('user_id', userId);
761
761
  const query = params.toString() ? `?${params.toString()}` : '';
@@ -763,6 +763,9 @@ export async function fetchUserPractices({ currentVersion, userId } = {}) {
763
763
  const response = await fetchDataHandler(url, currentVersion);
764
764
  const { data, version } = response;
765
765
  const userPractices = data;
766
+ if(!userPractices ) {
767
+ return { data: { practices: {} }, version };
768
+ }
766
769
 
767
770
  const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
768
771
 
File without changes
@@ -405,6 +405,8 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
405
405
  "type": _type,
406
406
  web_url_path,
407
407
  "permission_id": permission[]->railcontent_id,
408
+ live_event_start_time,
409
+ live_event_end_time,
408
410
  "isLive": live_event_start_time <= '${now}' && live_event_end_time >= '${now}'`
409
411
  const query = buildRawQuery(
410
412
  `defined(live_event_start_time) && (!defined(live_event_end_time) || live_event_end_time >= '${now}' ) && brand == '${brand}' && published_on > '${now}' && status == 'scheduled'`,
@@ -510,7 +512,7 @@ export async function fetchByRailContentId(id, contentType) {
510
512
  * .then(contents => console.log(contents))
511
513
  * .catch(error => console.error(error));
512
514
  */
513
- export async function fetchByRailContentIds(ids, contentType = undefined, brand= undefined) {
515
+ export async function fetchByRailContentIds(ids, contentType = undefined, brand = undefined) {
514
516
  if (!ids) {
515
517
  return []
516
518
  }
@@ -1318,7 +1320,7 @@ export async function fetchLessonContent(railContentId) {
1318
1320
  })
1319
1321
  const chapterProcess = (result) => {
1320
1322
  const now = getSanityDate(new Date(), false)
1321
- if(result.live_event_start_time && result.live_event_start_time){
1323
+ if (result.live_event_start_time && result.live_event_end_time) {
1322
1324
  result.isLive = result.live_event_start_time <= now && result.live_event_end_time >= now
1323
1325
  }
1324
1326
  const chapters = result.chapters ?? []
@@ -1334,15 +1336,23 @@ export async function fetchLessonContent(railContentId) {
1334
1336
  }
1335
1337
 
1336
1338
  /**
1339
+ * Returns a list of recommended content based on the provided railContentId.
1340
+ * If no recommendations found in recsys, falls back to fetching related lessons.
1337
1341
  *
1338
1342
  * @param railContentId
1339
1343
  * @param brand
1340
1344
  * @param count
1341
- * @returns {Promise<Array<Object>|null>}
1345
+ * @returns {Promise<Array<Object>>}
1342
1346
  */
1343
1347
  export async function fetchRelatedRecommendedContent(railContentId, brand, count = 10) {
1344
1348
  const recommendedItems = await fetchSimilarItems(railContentId, brand, count)
1345
- return fetchByRailContentIds(recommendedItems)
1349
+ if (recommendedItems && recommendedItems.length > 0) {
1350
+ return fetchByRailContentIds(recommendedItems)
1351
+ }
1352
+
1353
+ return await fetchRelatedLessons(railContentId, brand).then((result) =>
1354
+ result.related_lessons?.splice(0, count)
1355
+ )
1346
1356
  }
1347
1357
 
1348
1358
  /**
@@ -1412,18 +1422,20 @@ async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, cou
1412
1422
  */
1413
1423
  export async function fetchRelatedLessons(railContentId, brand) {
1414
1424
  const filterSameTypeAndSortOrder = await new FilterBuilder(
1415
- `_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`,
1425
+ `_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`
1416
1426
  ).buildFilter()
1417
1427
  const filterSameType = await new FilterBuilder(
1418
- `_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`,
1428
+ `_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`
1419
1429
  ).buildFilter()
1420
1430
  const filterSongSameArtist = await new FilterBuilder(
1421
- `_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`,
1431
+ `_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`
1422
1432
  ).buildFilter()
1423
1433
  const filterSongSameGenre = await new FilterBuilder(
1424
- `_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`,
1434
+ `_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`
1425
1435
  ).buildFilter()
1426
- const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`, {pullFutureContent: true}).buildFilter()
1436
+ const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`, {
1437
+ pullFutureContent: true,
1438
+ }).buildFilter()
1427
1439
  const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1428
1440
  const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, "genre": genre[]->name`
1429
1441
  const queryFieldsWithSort = queryFields + ', sort'
@@ -1444,15 +1456,15 @@ export async function fetchRelatedLessons(railContentId, brand) {
1444
1456
  let result = await fetchSanity(query, false)
1445
1457
 
1446
1458
  //there's no way in sanity to retrieve the index of an array, so we must calculate after fetch
1447
- if (result['for-calculations']['parents-list']) {
1459
+ if (result['for-calculations'] && result['for-calculations']['parents-list']) {
1448
1460
  const calc = result['for-calculations']
1449
1461
  const parentCount = calc['parents-list'].length
1450
- const currentParent = (calc['parents-list'].indexOf(result['parent_id']) + 1)
1462
+ const currentParent = calc['parents-list'].indexOf(result['parent_id']) + 1
1451
1463
  const siblingCount = calc['siblings-list'].length
1452
- const currentSibling = (calc['siblings-list'].indexOf(result['railcontent_id']) + 1)
1464
+ const currentSibling = calc['siblings-list'].indexOf(result['railcontent_id']) + 1
1453
1465
 
1454
1466
  delete result['for-calculations']
1455
- result = {...result, parentCount, currentParent, siblingCount, currentSibling}
1467
+ result = { ...result, parentCount, currentParent, siblingCount, currentSibling }
1456
1468
  return result
1457
1469
  } else {
1458
1470
  delete result['for-calculations']
@@ -1502,6 +1514,7 @@ export async function fetchPackAll(railcontentId, type = 'pack') {
1502
1514
  }
1503
1515
 
1504
1516
  export async function fetchLiveEvent(brand, forcedContentId = null) {
1517
+ const LIVE_EXTRA_MINUTES = 30
1505
1518
  //calendarIDs taken from addevent.php
1506
1519
  // TODO import instructor calendars to Sanity
1507
1520
  let defaultCalendarID = ''
@@ -1523,8 +1536,11 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1523
1536
  }
1524
1537
  let startDateTemp = new Date()
1525
1538
  let endDateTemp = new Date()
1526
- startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + 15))
1527
- endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15))
1539
+
1540
+ startDateTemp = new Date(
1541
+ startDateTemp.setMinutes(startDateTemp.getMinutes() + LIVE_EXTRA_MINUTES)
1542
+ )
1543
+ endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - LIVE_EXTRA_MINUTES))
1528
1544
 
1529
1545
  // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
1530
1546
  // this has some +- on times
@@ -2390,17 +2406,13 @@ export async function fetchScheduledAndNewReleases(
2390
2406
  return fetchSanity(query, true)
2391
2407
  }
2392
2408
 
2393
- export async function fetchShows(
2394
- brand,
2395
- type,
2396
- sort = 'sort'
2397
- ) {
2409
+ export async function fetchShows(brand, type, sort = 'sort') {
2398
2410
  const sortOrder = getSortOrder(sort, brand)
2399
2411
  const filter = `_type == '${type}' && brand == '${brand}'`
2400
2412
  const filterParams = {}
2401
2413
 
2402
2414
  const query = await buildQuery(filter, filterParams, getFieldsForContentType(type), {
2403
- sortOrder: sortOrder
2415
+ sortOrder: sortOrder,
2404
2416
  })
2405
2417
  return fetchSanity(query, true)
2406
2418
  }
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
@@ -55,7 +55,7 @@ function getIndefiniteArticle(streak) {
55
55
 
56
56
  export async function getUserPractices(userId = globalConfig.sessionConfig.userId) {
57
57
  if (userId !== globalConfig.sessionConfig.userId) {
58
- let data = await fetchUserPractices({ userId })
58
+ let data = await fetchUserPractices(0, { userId: userId })
59
59
  return data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
60
60
  } else {
61
61
  let data = await userActivityContext.getData()
@@ -376,20 +376,30 @@ export async function removeUserPractice(id) {
376
376
  export async function restoreUserPractice(id) {
377
377
  let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`
378
378
  const response = await fetchHandler(url, 'put')
379
- if (response?.data) {
380
- await userActivityContext.updateLocal(async function (localContext) {
381
- const restoredPractice = response.data
382
- const { date } = restoredPractice
383
- if (!localContext.data[DATA_KEY_PRACTICES][date]) {
384
- localContext.data[DATA_KEY_PRACTICES][date] = []
385
- }
386
- localContext.data[DATA_KEY_PRACTICES][date].push({
387
- id: restoredPractice.id,
388
- duration_seconds: restoredPractice.duration_seconds,
379
+ if (response?.data?.length) {
380
+ const restoredPractice = response.data.find((p) => p.id === id)
381
+ if (restoredPractice) {
382
+ await userActivityContext.updateLocal(async function (localContext) {
383
+ const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
384
+ const utcDate = new Date(restoredPractice.day)
385
+ const localDate = convertToTimeZone(utcDate, userTimeZone)
386
+ const date =
387
+ localDate.getFullYear() + '-' +
388
+ String(localDate.getMonth() + 1).padStart(2, '0') + '-' +
389
+ String(localDate.getDate()).padStart(2, '0')
390
+ if (localContext.data[DATA_KEY_PRACTICES][date]) {
391
+ localContext.data[DATA_KEY_PRACTICES][date] = []
392
+ }
393
+ response.data.forEach((restoredPractice) => {
394
+ localContext.data[DATA_KEY_PRACTICES][date].push({
395
+ id: restoredPractice.id,
396
+ duration_seconds: restoredPractice.duration_seconds,
397
+ })
398
+ })
389
399
  })
390
- })
400
+ }
391
401
  }
392
- const formattedMeta = await formatPracticeMeta(response.data)
402
+ const formattedMeta = await formatPracticeMeta(response.data || [])
393
403
  const practiceDuration = formattedMeta.reduce(
394
404
  (total, practice) => total + (practice.duration || 0),
395
405
  0
@@ -603,7 +613,7 @@ function getStreaksAndMessage(practices) {
603
613
  async function getUserPracticeIds(day = new Date().toISOString().split('T')[0], userId = null) {
604
614
  let practices = {}
605
615
  if (userId !== globalConfig.sessionConfig.userId) {
606
- let data = await fetchUserPractices({ userId })
616
+ let data = await fetchUserPractices(0, { userId: userId })
607
617
  practices = data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
608
618
  } else {
609
619
  let data = await userActivityContext.getData()
File without changes