musora-content-services 2.30.3 → 2.30.5
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/CHANGELOG.md +16 -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 +0 -0
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +0 -0
- package/src/index.js +0 -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/services/api/types.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/contentLikes.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/gamification.js +0 -0
- package/src/services/imageSRCBuilder.js +0 -0
- package/src/services/imageSRCVerify.js +0 -0
- package/src/services/sanity.js +2 -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 +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.js +0 -0
- package/src/services/user/user-management-system.js +0 -0
- package/src/services/userActivity.js +191 -143
- 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/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
package/.coderabbit.yaml
CHANGED
|
File without changes
|
package/.editorconfig
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/.prettierignore
CHANGED
|
File without changes
|
package/.prettierrc
CHANGED
|
File without changes
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
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.30.5](https://github.com/railroadmedia/musora-content-services/compare/v2.30.4...v2.30.5) (2025-08-06)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **MU2-918:** practice meta data for manual only sessions ([#402](https://github.com/railroadmedia/musora-content-services/issues/402)) ([a911529](https://github.com/railroadmedia/musora-content-services/commit/a9115297f00b6aed5f0ad73714cb860cfd71f331))
|
|
11
|
+
* **MU2-918:** safer .find use ([#405](https://github.com/railroadmedia/musora-content-services/issues/405)) ([ce81469](https://github.com/railroadmedia/musora-content-services/commit/ce814692a7aa46ee814c28ecae1ec404a0c09046))
|
|
12
|
+
|
|
13
|
+
### [2.30.4](https://github.com/railroadmedia/musora-content-services/compare/v2.30.3...v2.30.4) (2025-08-05)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **MU2-912:** pagination for Incomplete and Completed tabs ([#396](https://github.com/railroadmedia/musora-content-services/issues/396)) ([b66d574](https://github.com/railroadmedia/musora-content-services/commit/b66d57483df4bdd158a985bade3cf63fa3aa65bb))
|
|
19
|
+
* **MU2-916:** Add navigateTo and parentId to practice tracker and recent activity ([#398](https://github.com/railroadmedia/musora-content-services/issues/398)) ([6e43768](https://github.com/railroadmedia/musora-content-services/commit/6e437682f09655cc41966277c967c304b275d0ea))
|
|
20
|
+
|
|
5
21
|
### [2.30.3](https://github.com/railroadmedia/musora-content-services/compare/v2.30.2...v2.30.3) (2025-08-04)
|
|
6
22
|
|
|
7
23
|
### [2.30.2](https://github.com/railroadmedia/musora-content-services/compare/v2.30.1...v2.30.2) (2025-08-04)
|
package/README.md
CHANGED
|
File without changes
|
package/babel.config.cjs
CHANGED
|
File without changes
|
|
File without changes
|
package/docs/Gamification.html
CHANGED
|
File without changes
|
|
File without changes
|
package/docs/api_types.js.html
CHANGED
|
File without changes
|
package/docs/config.js.html
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/content.js.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
|
package/docs/global.html
CHANGED
|
File without changes
|
package/docs/index.html
CHANGED
|
File without changes
|
package/docs/module-Awards.html
CHANGED
|
File without changes
|
package/docs/module-Config.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
|
package/docs/railcontent.js.html
CHANGED
|
File without changes
|
package/docs/sanity.js.html
CHANGED
|
File without changes
|
package/docs/scripts/collapse.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/scripts/nav.js
CHANGED
|
File without changes
|
package/docs/scripts/polyfill.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/scripts/search.js
CHANGED
|
File without changes
|
package/docs/styles/jsdoc.css
CHANGED
|
File without changes
|
package/docs/styles/prettify.css
CHANGED
|
File without changes
|
|
File without changes
|
package/docs/user_chat.js.html
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/docs/user_types.js.html
CHANGED
|
File without changes
|
|
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
|
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
|
package/src/lib/httpHelper.js
CHANGED
|
File without changes
|
package/src/lib/lastUpdated.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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
|
package/src/services/sanity.js
CHANGED
|
@@ -2173,10 +2173,12 @@ export async function fetchTabData(
|
|
|
2173
2173
|
case 'incomplete':
|
|
2174
2174
|
progressIds = await getAllStarted();
|
|
2175
2175
|
sortOrder = null;
|
|
2176
|
+
withoutPagination = true;
|
|
2176
2177
|
break;
|
|
2177
2178
|
case 'completed':
|
|
2178
2179
|
progressIds = await getAllCompleted();
|
|
2179
2180
|
sortOrder = null;
|
|
2181
|
+
withoutPagination = true;
|
|
2180
2182
|
break;
|
|
2181
2183
|
}
|
|
2182
2184
|
|
|
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
|
|
@@ -35,7 +35,7 @@ import { TabResponseType } from '../contentMetaData'
|
|
|
35
35
|
import dayjs from 'dayjs'
|
|
36
36
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
|
|
37
37
|
import weekOfYear from 'dayjs/plugin/weekOfYear'
|
|
38
|
-
import {addContextToContent} from
|
|
38
|
+
import { addContextToContent } from './contentAggregator.js'
|
|
39
39
|
|
|
40
40
|
const DATA_KEY_PRACTICES = 'practices'
|
|
41
41
|
const DATA_KEY_LAST_UPDATED_TIME = 'u'
|
|
@@ -407,7 +407,7 @@ export async function restoreUserPractice(id) {
|
|
|
407
407
|
const restoredPractice = response.data.find((p) => p.id === id)
|
|
408
408
|
if (restoredPractice) {
|
|
409
409
|
await userActivityContext.updateLocal(async function (localContext) {
|
|
410
|
-
if (localContext.data[DATA_KEY_PRACTICES][restoredPractice.day]) {
|
|
410
|
+
if (!localContext.data[DATA_KEY_PRACTICES][restoredPractice.day]) {
|
|
411
411
|
localContext.data[DATA_KEY_PRACTICES][restoredPractice.day] = []
|
|
412
412
|
}
|
|
413
413
|
response.data.forEach((restoredPractice) => {
|
|
@@ -581,7 +581,21 @@ export async function getPracticeNotes(day) {
|
|
|
581
581
|
* .catch(error => console.error("Failed to get recent activity:", error));
|
|
582
582
|
*/
|
|
583
583
|
export async function getRecentActivity({ page = 1, limit = 5, tabName = null } = {}) {
|
|
584
|
-
|
|
584
|
+
const recentActivityData = await fetchRecentUserActivities({ page, limit, tabName })
|
|
585
|
+
const contentIds = recentActivityData.data.map((p) => p.contentId).filter((id) => id !== null)
|
|
586
|
+
const contents = await addContextToContent(fetchByRailContentIds, contentIds, {
|
|
587
|
+
addNavigateTo: true,
|
|
588
|
+
addNextLesson: true,
|
|
589
|
+
})
|
|
590
|
+
recentActivityData.data = recentActivityData.data.map((practice) => {
|
|
591
|
+
const content = contents?.find((c) => c.id === practice.contentId) || {}
|
|
592
|
+
return {
|
|
593
|
+
...practice,
|
|
594
|
+
parent_id: content.parent_id || null,
|
|
595
|
+
navigateTo: content.navigateTo,
|
|
596
|
+
}
|
|
597
|
+
})
|
|
598
|
+
return recentActivityData
|
|
585
599
|
}
|
|
586
600
|
|
|
587
601
|
/**
|
|
@@ -839,12 +853,15 @@ export async function calculateLongestStreaks(userId = globalConfig.sessionConfi
|
|
|
839
853
|
}
|
|
840
854
|
}
|
|
841
855
|
|
|
842
|
-
async function formatPracticeMeta(practices) {
|
|
856
|
+
async function formatPracticeMeta(practices = []) {
|
|
843
857
|
const contentIds = practices.map((p) => p.content_id).filter((id) => id !== null)
|
|
844
|
-
const contents = await fetchByRailContentIds
|
|
858
|
+
const contents = await addContextToContent(fetchByRailContentIds, contentIds, {
|
|
859
|
+
addNavigateTo: true,
|
|
860
|
+
addNextLesson: true,
|
|
861
|
+
})
|
|
845
862
|
|
|
846
863
|
return practices.map((practice) => {
|
|
847
|
-
const content = contents.find((c) => c.id === practice.content_id)
|
|
864
|
+
const content = contents && contents.length > 0 ? contents.find((c) => c.id === practice.content_id) : {}
|
|
848
865
|
|
|
849
866
|
return {
|
|
850
867
|
id: practice.id,
|
|
@@ -853,16 +870,18 @@ async function formatPracticeMeta(practices) {
|
|
|
853
870
|
thumbnail_url: practice.content_id ? content.thumbnail : practice.thumbnail_url || '',
|
|
854
871
|
duration: practice.duration_seconds || 0,
|
|
855
872
|
duration_seconds: practice.duration_seconds || 0,
|
|
856
|
-
content_url: content
|
|
873
|
+
content_url: content?.url || null,
|
|
857
874
|
title: practice.content_id ? content.title : practice.title,
|
|
858
875
|
category_id: practice.category_id,
|
|
859
876
|
instrument_id: practice.instrument_id,
|
|
860
|
-
content_type: getFormattedType(content
|
|
877
|
+
content_type: getFormattedType(content?.type || '', content?.brand || null),
|
|
861
878
|
content_id: practice.content_id || null,
|
|
862
|
-
content_brand: content
|
|
879
|
+
content_brand: content?.brand || null,
|
|
863
880
|
created_at: dayjs(practice.created_at),
|
|
864
|
-
sanity_type: content
|
|
865
|
-
content_slug: content
|
|
881
|
+
sanity_type: content?.type || null,
|
|
882
|
+
content_slug: content?.slug || null,
|
|
883
|
+
parent_id: content?.parent_id || null,
|
|
884
|
+
navigateTo: content?.navigateTo || null,
|
|
866
885
|
}
|
|
867
886
|
})
|
|
868
887
|
}
|
|
@@ -911,24 +930,31 @@ export async function deleteUserActivity(id) {
|
|
|
911
930
|
return await fetchHandler(url, 'DELETE')
|
|
912
931
|
}
|
|
913
932
|
|
|
914
|
-
async function extractPinnedItemsAndSortAllItems(
|
|
933
|
+
async function extractPinnedItemsAndSortAllItems(
|
|
934
|
+
userPinnedItem,
|
|
935
|
+
contentsMap,
|
|
936
|
+
eligiblePlaylistItems,
|
|
937
|
+
pinnedGuidedCourse,
|
|
938
|
+
limit
|
|
939
|
+
) {
|
|
915
940
|
let pinnedItem = await popPinnedItemFromContentsOrPlaylistMap(
|
|
916
941
|
userPinnedItem,
|
|
917
942
|
contentsMap,
|
|
918
|
-
eligiblePlaylistItems
|
|
943
|
+
eligiblePlaylistItems
|
|
919
944
|
)
|
|
920
945
|
|
|
921
946
|
const guidedCourseID = pinnedGuidedCourse?.content_id
|
|
922
|
-
let combined = []
|
|
947
|
+
let combined = []
|
|
923
948
|
if (pinnedGuidedCourse) {
|
|
924
|
-
const guidedCourseContent =
|
|
925
|
-
|
|
949
|
+
const guidedCourseContent =
|
|
950
|
+
contentsMap.get(guidedCourseID) ??
|
|
951
|
+
(await addContextToContent(fetchByRailContentId, guidedCourseID, 'guided-course', {
|
|
926
952
|
addNextLesson: true,
|
|
927
953
|
addNavigateTo: true,
|
|
928
954
|
addProgressStatus: true,
|
|
929
955
|
addProgressPercentage: true,
|
|
930
956
|
addProgressTimestamp: true,
|
|
931
|
-
})
|
|
957
|
+
}))
|
|
932
958
|
contentsMap = popContentAndRemoveChildrenFromContentsMap(guidedCourseContent, contentsMap)
|
|
933
959
|
guidedCourseContent.pinned = true
|
|
934
960
|
combined.push(guidedCourseContent)
|
|
@@ -949,23 +975,26 @@ function generateContentsMap(contents, playlistsContents) {
|
|
|
949
975
|
'learning-path-course',
|
|
950
976
|
'learning-path-level',
|
|
951
977
|
'guided-course-part',
|
|
952
|
-
])
|
|
953
|
-
const existingShows = new Set()
|
|
954
|
-
const contentsMap = new Map()
|
|
955
|
-
const childToParentMap = {}
|
|
956
|
-
contents.forEach(content => {
|
|
978
|
+
])
|
|
979
|
+
const existingShows = new Set()
|
|
980
|
+
const contentsMap = new Map()
|
|
981
|
+
const childToParentMap = {}
|
|
982
|
+
contents.forEach((content) => {
|
|
957
983
|
if (Array.isArray(content.parent_content_data) && content.parent_content_data.length > 0) {
|
|
958
|
-
childToParentMap[content.id] =
|
|
984
|
+
childToParentMap[content.id] =
|
|
985
|
+
content.parent_content_data[content.parent_content_data.length - 1]
|
|
959
986
|
}
|
|
960
|
-
})
|
|
987
|
+
})
|
|
961
988
|
|
|
962
|
-
const allRecentTypeSet = new Set(
|
|
963
|
-
|
|
964
|
-
)
|
|
965
|
-
contents.forEach(content => {
|
|
989
|
+
const allRecentTypeSet = new Set(Object.values(recentTypes).flat())
|
|
990
|
+
contents.forEach((content) => {
|
|
966
991
|
const id = content.id
|
|
967
992
|
const type = content.type
|
|
968
|
-
if (
|
|
993
|
+
if (
|
|
994
|
+
excludedTypes.has(type) ||
|
|
995
|
+
(!allRecentTypeSet.has(type) && !showsLessonTypes.includes(type))
|
|
996
|
+
)
|
|
997
|
+
return
|
|
969
998
|
if (!childToParentMap[id]) {
|
|
970
999
|
// Shows don't have a parent to link them, but need to be handled as if they're a set of children
|
|
971
1000
|
if (!existingShows.has(type)) {
|
|
@@ -983,11 +1012,11 @@ function generateContentsMap(contents, playlistsContents) {
|
|
|
983
1012
|
for (const item of playlistsContents) {
|
|
984
1013
|
const contentId = item.id
|
|
985
1014
|
contentsMap.delete(contentId)
|
|
986
|
-
const parentIds = item.parent_content_data || []
|
|
987
|
-
parentIds.forEach(id => contentsMap.delete(id))
|
|
1015
|
+
const parentIds = item.parent_content_data || []
|
|
1016
|
+
parentIds.forEach((id) => contentsMap.delete(id))
|
|
988
1017
|
}
|
|
989
1018
|
}
|
|
990
|
-
return contentsMap
|
|
1019
|
+
return contentsMap
|
|
991
1020
|
}
|
|
992
1021
|
|
|
993
1022
|
/**
|
|
@@ -1004,19 +1033,21 @@ function generateContentsMap(contents, playlistsContents) {
|
|
|
1004
1033
|
* .catch(error => console.error(error));
|
|
1005
1034
|
*/
|
|
1006
1035
|
export async function getProgressRows({ brand = null, limit = 8 } = {}) {
|
|
1007
|
-
|
|
1008
1036
|
// TODO slice progress to a reasonable number, say 100
|
|
1009
|
-
const [recentPlaylists, progressContents, allPinnedGuidedCourse, userPinnedItem
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1037
|
+
const [recentPlaylists, progressContents, allPinnedGuidedCourse, userPinnedItem] =
|
|
1038
|
+
await Promise.all([
|
|
1039
|
+
fetchUserPlaylists(brand, { sort: '-last_progress', limit: limit }),
|
|
1040
|
+
getAllStartedOrCompleted({ onlyIds: false, brand: brand }),
|
|
1041
|
+
pinnedGuidedCourses(brand),
|
|
1042
|
+
getUserPinnedItem(brand),
|
|
1043
|
+
])
|
|
1015
1044
|
let pinnedGuidedCourse = allPinnedGuidedCourse?.[0] ?? null
|
|
1016
1045
|
|
|
1017
|
-
const playlists = recentPlaylists?.data || []
|
|
1018
|
-
const eligiblePlaylistItems = await getEligiblePlaylistItems(playlists)
|
|
1019
|
-
const playlistEngagedOnContents = eligiblePlaylistItems.map(
|
|
1046
|
+
const playlists = recentPlaylists?.data || []
|
|
1047
|
+
const eligiblePlaylistItems = await getEligiblePlaylistItems(playlists)
|
|
1048
|
+
const playlistEngagedOnContents = eligiblePlaylistItems.map(
|
|
1049
|
+
(item) => item.playlist.last_engaged_on
|
|
1050
|
+
)
|
|
1020
1051
|
|
|
1021
1052
|
const nonPlaylistContentIds = Object.keys(progressContents)
|
|
1022
1053
|
if (pinnedGuidedCourse) {
|
|
@@ -1025,7 +1056,7 @@ export async function getProgressRows({ brand = null, limit = 8 } = {}) {
|
|
|
1025
1056
|
if (userPinnedItem?.progressType === 'content') {
|
|
1026
1057
|
nonPlaylistContentIds.push(userPinnedItem.id)
|
|
1027
1058
|
}
|
|
1028
|
-
const [
|
|
1059
|
+
const [playlistsContents, contents] = await Promise.all([
|
|
1029
1060
|
addContextToContent(fetchByRailContentIds, playlistEngagedOnContents, 'progress-tracker', {
|
|
1030
1061
|
addNextLesson: true,
|
|
1031
1062
|
addNavigateTo: true,
|
|
@@ -1039,17 +1070,23 @@ export async function getProgressRows({ brand = null, limit = 8 } = {}) {
|
|
|
1039
1070
|
addProgressStatus: true,
|
|
1040
1071
|
addProgressPercentage: true,
|
|
1041
1072
|
addProgressTimestamp: true,
|
|
1042
|
-
})
|
|
1043
|
-
])
|
|
1044
|
-
const contentsMap = generateContentsMap(contents, playlistsContents)
|
|
1045
|
-
let combined = await extractPinnedItemsAndSortAllItems(
|
|
1073
|
+
}),
|
|
1074
|
+
])
|
|
1075
|
+
const contentsMap = generateContentsMap(contents, playlistsContents)
|
|
1076
|
+
let combined = await extractPinnedItemsAndSortAllItems(
|
|
1077
|
+
userPinnedItem,
|
|
1078
|
+
contentsMap,
|
|
1079
|
+
eligiblePlaylistItems,
|
|
1080
|
+
pinnedGuidedCourse,
|
|
1081
|
+
limit
|
|
1082
|
+
)
|
|
1046
1083
|
const results = await Promise.all(
|
|
1047
|
-
combined
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
: processContentItem(item)
|
|
1051
|
-
|
|
1052
|
-
)
|
|
1084
|
+
combined
|
|
1085
|
+
.slice(0, limit)
|
|
1086
|
+
.map((item) =>
|
|
1087
|
+
item.type === 'playlist' ? processPlaylistItem(item) : processContentItem(item)
|
|
1088
|
+
)
|
|
1089
|
+
)
|
|
1053
1090
|
console.log('HomePageProgressRows results: remove before merge', results)
|
|
1054
1091
|
return {
|
|
1055
1092
|
type: TabResponseType.PROGRESS_ROWS,
|
|
@@ -1066,118 +1103,132 @@ async function getUserPinnedItem(brand) {
|
|
|
1066
1103
|
}
|
|
1067
1104
|
|
|
1068
1105
|
async function processContentItem(content) {
|
|
1069
|
-
const contentType = getFormattedType(content.type, content.brand)
|
|
1106
|
+
const contentType = getFormattedType(content.type, content.brand)
|
|
1070
1107
|
const isLive = content.isLive ?? false
|
|
1071
1108
|
let ctaText = getDefaultCTATextForContent(content, contentType)
|
|
1072
1109
|
|
|
1073
1110
|
content.completed_children = await getCompletedChildren(content, contentType)
|
|
1074
1111
|
|
|
1075
1112
|
if (content.type === 'guided-course') {
|
|
1076
|
-
const nextLessonPublishedOn = content.children.find(
|
|
1113
|
+
const nextLessonPublishedOn = content.children.find(
|
|
1114
|
+
(child) => child.id === content.navigateTo.id
|
|
1115
|
+
)?.published_on
|
|
1077
1116
|
let isLocked = new Date(nextLessonPublishedOn) > new Date()
|
|
1078
1117
|
if (isLocked) {
|
|
1079
1118
|
content.is_locked = true
|
|
1080
|
-
const timeRemaining = getTimeRemainingUntilLocal(nextLessonPublishedOn, {
|
|
1119
|
+
const timeRemaining = getTimeRemainingUntilLocal(nextLessonPublishedOn, {
|
|
1120
|
+
withTotalSeconds: true,
|
|
1121
|
+
})
|
|
1081
1122
|
content.time_remaining_seconds = timeRemaining.totalSeconds
|
|
1082
1123
|
ctaText = 'Next lesson in ' + timeRemaining.formatted
|
|
1083
|
-
} else if (!content.progressStatus || content.progressStatus === 'not-started'
|
|
1084
|
-
ctaText =
|
|
1124
|
+
} else if (!content.progressStatus || content.progressStatus === 'not-started') {
|
|
1125
|
+
ctaText = 'Start Course'
|
|
1085
1126
|
}
|
|
1086
1127
|
}
|
|
1087
1128
|
|
|
1088
|
-
if (contentType === 'show'){
|
|
1129
|
+
if (contentType === 'show') {
|
|
1089
1130
|
const shows = await fetchShows(content.brand, content.type)
|
|
1090
|
-
const showIds = shows.map(item => item.id)
|
|
1091
|
-
const progressOnItems = await getProgressStateByIds(showIds)
|
|
1131
|
+
const showIds = shows.map((item) => item.id)
|
|
1132
|
+
const progressOnItems = await getProgressStateByIds(showIds)
|
|
1092
1133
|
const completedShows = content.completed_children
|
|
1093
1134
|
const progressTimestamp = content.progressTimestamp
|
|
1094
1135
|
const wasPinned = content.pinned ?? false
|
|
1095
1136
|
if (content.progressStatus === 'completed') {
|
|
1096
1137
|
// this could be handled more gracefully if their was a parent content type for shows
|
|
1097
|
-
const nextByProgress = findIncompleteLesson(progressOnItems, content.id, content.type)
|
|
1098
|
-
content = shows.find(lesson => lesson.id === nextByProgress)
|
|
1138
|
+
const nextByProgress = findIncompleteLesson(progressOnItems, content.id, content.type)
|
|
1139
|
+
content = shows.find((lesson) => lesson.id === nextByProgress)
|
|
1099
1140
|
content.completed_children = completedShows
|
|
1100
1141
|
content.progressTimestamp = progressTimestamp
|
|
1101
1142
|
content.pinned = wasPinned
|
|
1102
1143
|
}
|
|
1103
|
-
content.child_count = shows.length
|
|
1104
|
-
content.progressPercentage = Math.round((completedShows / shows.length) * 100)
|
|
1144
|
+
content.child_count = shows.length
|
|
1145
|
+
content.progressPercentage = Math.round((completedShows / shows.length) * 100)
|
|
1105
1146
|
if (completedShows === shows.length) {
|
|
1106
|
-
ctaText = 'Revisit Show'
|
|
1147
|
+
ctaText = 'Revisit Show'
|
|
1107
1148
|
}
|
|
1108
1149
|
}
|
|
1109
1150
|
|
|
1110
1151
|
return {
|
|
1111
|
-
id:
|
|
1112
|
-
progressType:
|
|
1113
|
-
header:
|
|
1114
|
-
pinned:
|
|
1115
|
-
content:
|
|
1116
|
-
body:
|
|
1117
|
-
progressPercent: isLive ? undefined: content.progressPercentage,
|
|
1118
|
-
thumbnail:
|
|
1119
|
-
title:
|
|
1120
|
-
isLive:
|
|
1121
|
-
badge:
|
|
1122
|
-
isLocked:
|
|
1123
|
-
subtitle:
|
|
1124
|
-
|
|
1125
|
-
|
|
1152
|
+
id: content.id,
|
|
1153
|
+
progressType: 'content',
|
|
1154
|
+
header: contentType,
|
|
1155
|
+
pinned: content.pinned ?? false,
|
|
1156
|
+
content: content,
|
|
1157
|
+
body: {
|
|
1158
|
+
progressPercent: isLive ? undefined : content.progressPercentage,
|
|
1159
|
+
thumbnail: content.thumbnail,
|
|
1160
|
+
title: content.title,
|
|
1161
|
+
isLive: isLive,
|
|
1162
|
+
badge: content.badge ?? null,
|
|
1163
|
+
isLocked: content.is_locked ?? false,
|
|
1164
|
+
subtitle:
|
|
1165
|
+
!content.child_count || content.lesson_count === 1
|
|
1166
|
+
? contentType === 'lesson' && isLive === false
|
|
1167
|
+
? `${content.progressPercentage}% Complete`
|
|
1168
|
+
: `${content.difficulty_string} • ${content.artist_name}`
|
|
1169
|
+
: `${content.completed_children} of ${content.lesson_count ?? content.child_count} Lessons Complete`,
|
|
1126
1170
|
},
|
|
1127
|
-
cta:
|
|
1128
|
-
text:
|
|
1171
|
+
cta: {
|
|
1172
|
+
text: ctaText,
|
|
1129
1173
|
timeRemainingToUnlockSeconds: content.time_remaining_seconds ?? null,
|
|
1130
1174
|
action: {
|
|
1131
|
-
type:
|
|
1175
|
+
type: content.type,
|
|
1132
1176
|
brand: content.brand,
|
|
1133
|
-
id:
|
|
1134
|
-
slug:
|
|
1177
|
+
id: content.id,
|
|
1178
|
+
slug: content.slug,
|
|
1135
1179
|
child: content.navigateTo,
|
|
1136
|
-
}
|
|
1180
|
+
},
|
|
1137
1181
|
},
|
|
1138
1182
|
// *1000 is to match playlists which are saved in millisecond accuracy
|
|
1139
|
-
progressTimestamp: content.progressTimestamp * 1000
|
|
1140
|
-
}
|
|
1183
|
+
progressTimestamp: content.progressTimestamp * 1000,
|
|
1184
|
+
}
|
|
1141
1185
|
}
|
|
1142
1186
|
|
|
1143
|
-
function getDefaultCTATextForContent(content, contentType)
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1187
|
+
function getDefaultCTATextForContent(content, contentType) {
|
|
1188
|
+
let ctaText = 'Continue'
|
|
1189
|
+
if (content.progressStatus === 'completed') {
|
|
1190
|
+
if (
|
|
1191
|
+
contentType === songs[content.brand] ||
|
|
1192
|
+
contentType === 'play along' ||
|
|
1193
|
+
contentType === 'jam track'
|
|
1194
|
+
)
|
|
1195
|
+
ctaText = 'Replay Song'
|
|
1196
|
+
if (contentType === 'lesson') ctaText = 'Revisit Lesson'
|
|
1197
|
+
if (contentType === 'song tutorial' || collectionLessonTypes.includes(contentType))
|
|
1198
|
+
ctaText = 'Revisit Lessons'
|
|
1151
1199
|
if (contentType === 'pack') ctaText = 'View Lessons'
|
|
1152
1200
|
}
|
|
1153
1201
|
return ctaText
|
|
1154
1202
|
}
|
|
1155
1203
|
|
|
1156
|
-
async function getCompletedChildren(content, contentType)
|
|
1157
|
-
{
|
|
1204
|
+
async function getCompletedChildren(content, contentType) {
|
|
1158
1205
|
let completedChildren = null
|
|
1159
1206
|
if (contentType === 'show') {
|
|
1160
1207
|
const shows = await addContextToContent(fetchShows, content.brand, content.type, {
|
|
1161
1208
|
addProgressStatus: true,
|
|
1162
1209
|
})
|
|
1163
|
-
completedChildren = Object.values(shows).filter(
|
|
1210
|
+
completedChildren = Object.values(shows).filter(
|
|
1211
|
+
(show) => show.progressStatus === 'completed'
|
|
1212
|
+
).length
|
|
1164
1213
|
} else if (content.lesson_count > 0) {
|
|
1165
|
-
const lessonIds = getLeafNodes(content)
|
|
1166
|
-
const progressOnItems = await getProgressStateByIds(lessonIds)
|
|
1167
|
-
completedChildren
|
|
1214
|
+
const lessonIds = getLeafNodes(content)
|
|
1215
|
+
const progressOnItems = await getProgressStateByIds(lessonIds)
|
|
1216
|
+
completedChildren = Object.values(progressOnItems).filter(
|
|
1217
|
+
(value) => value === 'completed'
|
|
1218
|
+
).length
|
|
1168
1219
|
}
|
|
1169
1220
|
return completedChildren
|
|
1170
1221
|
}
|
|
1171
1222
|
|
|
1172
1223
|
async function processPlaylistItem(item) {
|
|
1173
|
-
const playlist = item.playlist
|
|
1224
|
+
const playlist = item.playlist
|
|
1174
1225
|
return {
|
|
1175
|
-
id:
|
|
1176
|
-
progressType:
|
|
1177
|
-
header:
|
|
1178
|
-
pinned:
|
|
1179
|
-
playlist:
|
|
1180
|
-
body:
|
|
1226
|
+
id: playlist.id,
|
|
1227
|
+
progressType: 'playlist',
|
|
1228
|
+
header: 'playlist',
|
|
1229
|
+
pinned: item.pinned ?? false,
|
|
1230
|
+
playlist: playlist,
|
|
1231
|
+
body: {
|
|
1181
1232
|
first_items_thumbnail_url: playlist.first_items_thumbnail_url,
|
|
1182
1233
|
title: playlist.name,
|
|
1183
1234
|
subtitle: `${playlist.duration_formated} • ${playlist.total_items} items • ${playlist.likes} likes • ${playlist.user.display_name}`,
|
|
@@ -1187,14 +1238,14 @@ async function processPlaylistItem(item) {
|
|
|
1187
1238
|
cta: {
|
|
1188
1239
|
text: 'Continue',
|
|
1189
1240
|
action: {
|
|
1190
|
-
brand:
|
|
1241
|
+
brand: playlist.brand,
|
|
1191
1242
|
item_id: playlist.navigateTo.id ?? null,
|
|
1192
1243
|
content_id: playlist.navigateTo.content_id ?? null,
|
|
1193
|
-
type:
|
|
1244
|
+
type: 'playlists',
|
|
1194
1245
|
// TODO depreciated, maintained to avoid breaking changes
|
|
1195
|
-
id:
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1246
|
+
id: playlist.id,
|
|
1247
|
+
},
|
|
1248
|
+
},
|
|
1198
1249
|
}
|
|
1199
1250
|
}
|
|
1200
1251
|
|
|
@@ -1209,18 +1260,18 @@ const getFormattedType = (type, brand) => {
|
|
|
1209
1260
|
}
|
|
1210
1261
|
|
|
1211
1262
|
function getLeafNodes(content) {
|
|
1212
|
-
const ids = []
|
|
1263
|
+
const ids = []
|
|
1213
1264
|
function traverse(children) {
|
|
1214
1265
|
for (const item of children) {
|
|
1215
1266
|
if (item.children) {
|
|
1216
|
-
traverse(item.children)
|
|
1267
|
+
traverse(item.children) // Recursively handle nested lessons
|
|
1217
1268
|
} else if (item.id) {
|
|
1218
|
-
ids.push(item.id)
|
|
1269
|
+
ids.push(item.id)
|
|
1219
1270
|
}
|
|
1220
1271
|
}
|
|
1221
1272
|
}
|
|
1222
1273
|
if (content && Array.isArray(content.children)) {
|
|
1223
|
-
traverse(content.children)
|
|
1274
|
+
traverse(content.children)
|
|
1224
1275
|
}
|
|
1225
1276
|
return ids
|
|
1226
1277
|
}
|
|
@@ -1237,7 +1288,7 @@ async function getEligiblePlaylistItems(playlists) {
|
|
|
1237
1288
|
progressTimestamp: timestamp / 1000,
|
|
1238
1289
|
playlist: p,
|
|
1239
1290
|
id: p.id,
|
|
1240
|
-
}
|
|
1291
|
+
}
|
|
1241
1292
|
})
|
|
1242
1293
|
)
|
|
1243
1294
|
}
|
|
@@ -1247,7 +1298,7 @@ function mergeAndSortItems(items, limit) {
|
|
|
1247
1298
|
const deduped = []
|
|
1248
1299
|
|
|
1249
1300
|
for (const item of items) {
|
|
1250
|
-
const key = `${item.id}-${item.type}
|
|
1301
|
+
const key = `${item.id}-${item.type}`
|
|
1251
1302
|
if (!seen.has(key)) {
|
|
1252
1303
|
seen.add(key)
|
|
1253
1304
|
deduped.push(item)
|
|
@@ -1255,12 +1306,12 @@ function mergeAndSortItems(items, limit) {
|
|
|
1255
1306
|
}
|
|
1256
1307
|
|
|
1257
1308
|
return deduped
|
|
1258
|
-
.filter(item => typeof item.progressTimestamp === 'number' && item.progressTimestamp >= 0)
|
|
1309
|
+
.filter((item) => typeof item.progressTimestamp === 'number' && item.progressTimestamp >= 0)
|
|
1259
1310
|
.sort((a, b) => {
|
|
1260
|
-
if (a.pinned && !b.pinned) return -1
|
|
1261
|
-
if (!a.pinned && b.pinned) return 1
|
|
1311
|
+
if (a.pinned && !b.pinned) return -1
|
|
1312
|
+
if (!a.pinned && b.pinned) return 1
|
|
1262
1313
|
// TODO pinned guided course should always be before user pinned item
|
|
1263
|
-
return b.progressTimestamp - a.progressTimestamp
|
|
1314
|
+
return b.progressTimestamp - a.progressTimestamp
|
|
1264
1315
|
})
|
|
1265
1316
|
.slice(0, limit + 5)
|
|
1266
1317
|
}
|
|
@@ -1288,39 +1339,36 @@ export function findIncompleteLesson(progressOnItems, currentContentId, contentT
|
|
|
1288
1339
|
|
|
1289
1340
|
async function popPinnedItemFromContentsOrPlaylistMap(pinned, contentsMap, playlistItems) {
|
|
1290
1341
|
if (!pinned) return null
|
|
1291
|
-
const {id, progressType, pinnedAt} = pinned
|
|
1342
|
+
const { id, progressType, pinnedAt } = pinned
|
|
1292
1343
|
let item = null
|
|
1293
1344
|
if (progressType === 'content') {
|
|
1294
1345
|
const pinnedId = parseInt(id)
|
|
1295
1346
|
if (contentsMap.has(pinnedId)) {
|
|
1296
1347
|
item = contentsMap.get(pinnedId)
|
|
1297
1348
|
contentsMap.delete(pinnedId)
|
|
1298
|
-
|
|
1299
1349
|
} else {
|
|
1300
1350
|
// we use fetchByRailContentIds so that we don't have the _type restriction in the query
|
|
1301
1351
|
let data = await fetchByRailContentIds([id], 'progress-tracker')
|
|
1302
|
-
item = await addContextToContent(() => data[0] ?? null,
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
}
|
|
1310
|
-
)
|
|
1352
|
+
item = await addContextToContent(() => data[0] ?? null, {
|
|
1353
|
+
addNextLesson: true,
|
|
1354
|
+
addNavigateTo: true,
|
|
1355
|
+
addProgressStatus: true,
|
|
1356
|
+
addProgressPercentage: true,
|
|
1357
|
+
addProgressTimestamp: true,
|
|
1358
|
+
})
|
|
1311
1359
|
}
|
|
1312
1360
|
}
|
|
1313
1361
|
if (progressType === 'playlist') {
|
|
1314
|
-
const pinnedPlaylist = playlistItems.find(p => p.playlist.id === id)
|
|
1362
|
+
const pinnedPlaylist = playlistItems.find((p) => p.playlist.id === id)
|
|
1315
1363
|
if (pinnedPlaylist) {
|
|
1316
|
-
playlistItems = playlistItems.filter(p => p.playlist.id !== id)
|
|
1364
|
+
playlistItems = playlistItems.filter((p) => p.playlist.id !== id)
|
|
1317
1365
|
item = pinnedPlaylist
|
|
1318
1366
|
} else {
|
|
1319
1367
|
const playlist = await fetchPlaylist(id)
|
|
1320
1368
|
item = {
|
|
1321
|
-
id:
|
|
1322
|
-
playlist:
|
|
1323
|
-
type:
|
|
1369
|
+
id: id,
|
|
1370
|
+
playlist: playlist,
|
|
1371
|
+
type: 'playlist',
|
|
1324
1372
|
progressTimestamp: new Date(pinnedAt).getTime(),
|
|
1325
1373
|
}
|
|
1326
1374
|
}
|
|
@@ -1329,11 +1377,11 @@ async function popPinnedItemFromContentsOrPlaylistMap(pinned, contentsMap, playl
|
|
|
1329
1377
|
}
|
|
1330
1378
|
|
|
1331
1379
|
function popContentAndRemoveChildrenFromContentsMap(content, contentsMap) {
|
|
1332
|
-
const children = content.children.map(child => child.id)
|
|
1380
|
+
const children = content.children.map((child) => child.id)
|
|
1333
1381
|
if (contentsMap.has(content.id)) {
|
|
1334
1382
|
contentsMap.delete(content.id)
|
|
1335
1383
|
}
|
|
1336
|
-
children.forEach(child => {
|
|
1384
|
+
children.forEach((child) => {
|
|
1337
1385
|
if (contentsMap.has(child)) {
|
|
1338
1386
|
contentsMap.delete(child)
|
|
1339
1387
|
}
|
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
|
|
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
|