musora-content-services 1.0.119 → 1.0.121
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 +4 -0
- package/link_mcs.sh +0 -0
- package/package.json +1 -1
- package/src/contentTypeConfig.js +5 -0
- package/src/filterBuilder.js +2 -1
- package/src/index.d.ts +11 -21
- package/src/index.js +11 -21
- package/src/services/config.js +6 -2
- package/src/services/contentLikes.js +38 -0
- package/src/services/dataContext.js +88 -0
- package/src/services/railcontent.js +36 -46
- package/src/services/sanity.js +4 -1
- package/test/contentLikes.test.js +86 -0
- package/test/sanityQueryService.test.js +11 -0
- package/src/services/userContext.js +0 -105
- package/test/userContext.test.js +0 -86
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
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
|
+
### [1.0.121](https://github.com/railroadmedia/musora-content-services/compare/v1.0.120...v1.0.121) (2024-10-01)
|
|
6
|
+
|
|
7
|
+
### [1.0.120](https://github.com/railroadmedia/musora-content-services/compare/v1.0.119...v1.0.120) (2024-09-30)
|
|
8
|
+
|
|
5
9
|
### [1.0.119](https://github.com/railroadmedia/musora-content-services/compare/v1.0.118...v1.0.119) (2024-09-27)
|
|
6
10
|
|
|
7
11
|
### [1.0.118](https://github.com/railroadmedia/musora-content-services/compare/v1.0.117...v1.0.118) (2024-09-27)
|
package/link_mcs.sh
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/contentTypeConfig.js
CHANGED
|
@@ -95,6 +95,11 @@ let contentTypeConfig = {
|
|
|
95
95
|
'"instructors": instructor[]->name',
|
|
96
96
|
'"header_image_url": thumbnail.asset->url',
|
|
97
97
|
'"logo_image_url": logo_image_url.asset->url',
|
|
98
|
+
'"award": award.asset->url',
|
|
99
|
+
`"award_template": award_template[]{
|
|
100
|
+
streak_minimum,
|
|
101
|
+
"template" : template.asset->url,
|
|
102
|
+
}`,
|
|
98
103
|
]
|
|
99
104
|
},
|
|
100
105
|
'course': {
|
package/src/filterBuilder.js
CHANGED
|
@@ -22,7 +22,8 @@ export class FilterBuilder {
|
|
|
22
22
|
this.getFutureContentOnly = getFutureContentOnly;
|
|
23
23
|
this.getFutureScheduledContentsOnly = getFutureScheduledContentsOnly;
|
|
24
24
|
this.filter = filter;
|
|
25
|
-
this.debug = process.env.DEBUG === 'true' || false;
|
|
25
|
+
// this.debug = process.env.DEBUG === 'true' || false;
|
|
26
|
+
this.debug = false;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
|
package/src/index.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ import {
|
|
|
5
5
|
initializeService
|
|
6
6
|
} from './services/config.js';
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
isContentLiked,
|
|
10
|
+
likeContent,
|
|
11
|
+
unlikeContent
|
|
12
|
+
} from './services/contentLikes.js';
|
|
13
|
+
|
|
8
14
|
import {
|
|
9
15
|
fetchAllCompletedStates,
|
|
10
16
|
fetchCompletedContent,
|
|
@@ -12,10 +18,9 @@ import {
|
|
|
12
18
|
fetchContentInProgress,
|
|
13
19
|
fetchContentPageUserData,
|
|
14
20
|
fetchHandler,
|
|
15
|
-
fetchLikeContent,
|
|
16
21
|
fetchSongsInProgress,
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
fetchUserLikes,
|
|
23
|
+
fetchUserPermissions,
|
|
19
24
|
fetchVimeoData
|
|
20
25
|
} from './services/railcontent.js';
|
|
21
26
|
|
|
@@ -65,19 +70,8 @@ import {
|
|
|
65
70
|
getSortOrder
|
|
66
71
|
} from './services/sanity.js';
|
|
67
72
|
|
|
68
|
-
import {
|
|
69
|
-
clearCache,
|
|
70
|
-
fetchContentData,
|
|
71
|
-
init,
|
|
72
|
-
likeContent,
|
|
73
|
-
testClearLocal,
|
|
74
|
-
unlikeContent,
|
|
75
|
-
version
|
|
76
|
-
} from './services/userContext.js';
|
|
77
|
-
|
|
78
73
|
declare module 'musora-content-services' {
|
|
79
74
|
export {
|
|
80
|
-
clearCache,
|
|
81
75
|
fetchAll,
|
|
82
76
|
fetchAllCompletedStates,
|
|
83
77
|
fetchAllFilterOptions,
|
|
@@ -94,7 +88,6 @@ declare module 'musora-content-services' {
|
|
|
94
88
|
fetchCoachLessons,
|
|
95
89
|
fetchCompletedContent,
|
|
96
90
|
fetchCompletedState,
|
|
97
|
-
fetchContentData,
|
|
98
91
|
fetchContentInProgress,
|
|
99
92
|
fetchContentPageUserData,
|
|
100
93
|
fetchCourseOverview,
|
|
@@ -102,7 +95,6 @@ declare module 'musora-content-services' {
|
|
|
102
95
|
fetchGenreLessons,
|
|
103
96
|
fetchHandler,
|
|
104
97
|
fetchLessonContent,
|
|
105
|
-
fetchLikeContent,
|
|
106
98
|
fetchLiveEvent,
|
|
107
99
|
fetchMetadata,
|
|
108
100
|
fetchMethod,
|
|
@@ -127,18 +119,16 @@ declare module 'musora-content-services' {
|
|
|
127
119
|
fetchSongCount,
|
|
128
120
|
fetchSongFilterOptions,
|
|
129
121
|
fetchSongsInProgress,
|
|
130
|
-
fetchUnlikeContent,
|
|
131
122
|
fetchUpcomingEvents,
|
|
132
|
-
|
|
123
|
+
fetchUserLikes,
|
|
124
|
+
fetchUserPermissions,
|
|
133
125
|
fetchVimeoData,
|
|
134
126
|
fetchWorkouts,
|
|
135
127
|
getSortOrder,
|
|
136
128
|
globalConfig,
|
|
137
|
-
init,
|
|
138
129
|
initializeService,
|
|
130
|
+
isContentLiked,
|
|
139
131
|
likeContent,
|
|
140
|
-
testClearLocal,
|
|
141
132
|
unlikeContent,
|
|
142
|
-
version,
|
|
143
133
|
}
|
|
144
134
|
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,12 @@ import {
|
|
|
5
5
|
initializeService
|
|
6
6
|
} from './services/config.js';
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
isContentLiked,
|
|
10
|
+
likeContent,
|
|
11
|
+
unlikeContent
|
|
12
|
+
} from './services/contentLikes.js';
|
|
13
|
+
|
|
8
14
|
import {
|
|
9
15
|
fetchAllCompletedStates,
|
|
10
16
|
fetchCompletedContent,
|
|
@@ -12,10 +18,9 @@ import {
|
|
|
12
18
|
fetchContentInProgress,
|
|
13
19
|
fetchContentPageUserData,
|
|
14
20
|
fetchHandler,
|
|
15
|
-
fetchLikeContent,
|
|
16
21
|
fetchSongsInProgress,
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
fetchUserLikes,
|
|
23
|
+
fetchUserPermissions,
|
|
19
24
|
fetchVimeoData
|
|
20
25
|
} from './services/railcontent.js';
|
|
21
26
|
|
|
@@ -65,18 +70,7 @@ import {
|
|
|
65
70
|
getSortOrder
|
|
66
71
|
} from './services/sanity.js';
|
|
67
72
|
|
|
68
|
-
import {
|
|
69
|
-
clearCache,
|
|
70
|
-
fetchContentData,
|
|
71
|
-
init,
|
|
72
|
-
likeContent,
|
|
73
|
-
testClearLocal,
|
|
74
|
-
unlikeContent,
|
|
75
|
-
version
|
|
76
|
-
} from './services/userContext.js';
|
|
77
|
-
|
|
78
73
|
export {
|
|
79
|
-
clearCache,
|
|
80
74
|
fetchAll,
|
|
81
75
|
fetchAllCompletedStates,
|
|
82
76
|
fetchAllFilterOptions,
|
|
@@ -93,7 +87,6 @@ export {
|
|
|
93
87
|
fetchCoachLessons,
|
|
94
88
|
fetchCompletedContent,
|
|
95
89
|
fetchCompletedState,
|
|
96
|
-
fetchContentData,
|
|
97
90
|
fetchContentInProgress,
|
|
98
91
|
fetchContentPageUserData,
|
|
99
92
|
fetchCourseOverview,
|
|
@@ -101,7 +94,6 @@ export {
|
|
|
101
94
|
fetchGenreLessons,
|
|
102
95
|
fetchHandler,
|
|
103
96
|
fetchLessonContent,
|
|
104
|
-
fetchLikeContent,
|
|
105
97
|
fetchLiveEvent,
|
|
106
98
|
fetchMetadata,
|
|
107
99
|
fetchMethod,
|
|
@@ -126,17 +118,15 @@ export {
|
|
|
126
118
|
fetchSongCount,
|
|
127
119
|
fetchSongFilterOptions,
|
|
128
120
|
fetchSongsInProgress,
|
|
129
|
-
fetchUnlikeContent,
|
|
130
121
|
fetchUpcomingEvents,
|
|
131
|
-
|
|
122
|
+
fetchUserLikes,
|
|
123
|
+
fetchUserPermissions,
|
|
132
124
|
fetchVimeoData,
|
|
133
125
|
fetchWorkouts,
|
|
134
126
|
getSortOrder,
|
|
135
127
|
globalConfig,
|
|
136
|
-
init,
|
|
137
128
|
initializeService,
|
|
129
|
+
isContentLiked,
|
|
138
130
|
likeContent,
|
|
139
|
-
testClearLocal,
|
|
140
131
|
unlikeContent,
|
|
141
|
-
version,
|
|
142
132
|
};
|
package/src/services/config.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
let globalConfig = {
|
|
6
6
|
sanityConfig: {},
|
|
7
|
-
railcontentConfig: {}
|
|
7
|
+
railcontentConfig: {},
|
|
8
|
+
localStorage: null
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -24,6 +25,7 @@ let globalConfig = {
|
|
|
24
25
|
* @param {string} config.railcontentConfig.token - The token for authenticating user-specific requests.
|
|
25
26
|
* @param {string} config.railcontentConfig.userId - The user ID for fetching user-specific data.
|
|
26
27
|
* @param {string} config.railcontentConfig.baseUrl - The url for the enviroment.
|
|
28
|
+
* @param {Object} config.localStorage - Cache to use for localStorage
|
|
27
29
|
|
|
28
30
|
*
|
|
29
31
|
* @example
|
|
@@ -41,12 +43,14 @@ let globalConfig = {
|
|
|
41
43
|
* token: 'your-user-api-token',
|
|
42
44
|
* userId: 'current-user-id',
|
|
43
45
|
* baseUrl: 'https://web-staging-one.musora.com'
|
|
44
|
-
* }
|
|
46
|
+
* },
|
|
47
|
+
* localStorage: localStorage
|
|
45
48
|
* });
|
|
46
49
|
*/
|
|
47
50
|
function initializeService(config) {
|
|
48
51
|
globalConfig.sanityConfig = config.sanityConfig;
|
|
49
52
|
globalConfig.railcontentConfig = config.railcontentConfig;
|
|
53
|
+
globalConfig.localStorage = config.localStorage;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
// Export both the initialization function and the config object
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {fetchUserLikes, postContentLiked, postContentUnliked} from "./railcontent";
|
|
2
|
+
import {DataContext, ContentVersionKey} from "./dataContext";
|
|
3
|
+
|
|
4
|
+
export let dataContext = new DataContext(ContentVersionKey, fetchUserLikes);
|
|
5
|
+
|
|
6
|
+
export async function isContentLiked(contentId) {
|
|
7
|
+
let data = await dataContext.getData();
|
|
8
|
+
return data.includes(contentId);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function likeContent(contentId) {
|
|
12
|
+
await dataContext.update(
|
|
13
|
+
function (context) {
|
|
14
|
+
if (!context.data.includes(contentId)) {
|
|
15
|
+
context.data.push(contentId);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
async function(){
|
|
19
|
+
return postContentLiked(contentId);
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function unlikeContent(contentId) {
|
|
25
|
+
await dataContext.update(
|
|
26
|
+
function (context) {
|
|
27
|
+
if (context.data.includes(contentId)) {
|
|
28
|
+
const index = context.data.indexOf(contentId);
|
|
29
|
+
if (index > -1) { // only splice array when item is found
|
|
30
|
+
context.data.splice(index, 1); // 2nd parameter means remove one item only
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
async function(){
|
|
35
|
+
return postContentUnliked(contentId);
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {globalConfig} from "./config";
|
|
2
|
+
|
|
3
|
+
//These constants need to match MWP UserDataVersionKeyEnum enum
|
|
4
|
+
export const ContentVersionKey = 0;
|
|
5
|
+
|
|
6
|
+
let cache = null;
|
|
7
|
+
|
|
8
|
+
export class DataContext {
|
|
9
|
+
context = null;
|
|
10
|
+
|
|
11
|
+
constructor(dataVersionKey, fetchDataFunction) {
|
|
12
|
+
this.dataVersionKey = dataVersionKey;
|
|
13
|
+
this.fetchDataFunction = fetchDataFunction;
|
|
14
|
+
this.localStorageKey = `dataContext_${this.dataVersionKey}`;
|
|
15
|
+
this.localStorageLastUpdatedKey = `dataContext_${this.dataVersionKey}_lastUpdated`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async getData() {
|
|
19
|
+
this.ensureLocalContextLoaded();
|
|
20
|
+
if (!this.context || this.shouldVerifyServerVerions()) {
|
|
21
|
+
let version = this.version();
|
|
22
|
+
let data = await this.fetchData(version);
|
|
23
|
+
if (data.version !== "No Change") {
|
|
24
|
+
this.context = data;
|
|
25
|
+
cache.setItem(this.localStorageKey, JSON.stringify(data));
|
|
26
|
+
}
|
|
27
|
+
cache.setItem(this.localStorageLastUpdatedKey, new Date().getTime());
|
|
28
|
+
}
|
|
29
|
+
return this.context.data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async fetchData(version) {
|
|
33
|
+
return await this.fetchDataFunction(version);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
ensureLocalContextLoaded() {
|
|
37
|
+
if (this.context) return;
|
|
38
|
+
this.verifyConfig();
|
|
39
|
+
let localData = cache.getItem(this.localStorageKey);
|
|
40
|
+
if (localData) {
|
|
41
|
+
this.context = JSON.parse(localData);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
verifyConfig() {
|
|
46
|
+
if (!cache) {
|
|
47
|
+
cache = globalConfig.localStorage;
|
|
48
|
+
if (!cache) throw new Error('dataContext: LocalStorage cache not configured in musora content services initializeService.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
shouldVerifyServerVerions() {
|
|
53
|
+
let lastUpdated = cache.getItem(this.localStorageLastUpdatedKey);
|
|
54
|
+
if (!lastUpdated) return false;
|
|
55
|
+
const verifyServerTime = 10000; //10 s
|
|
56
|
+
return (new Date().getTime() - lastUpdated) > verifyServerTime;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
clearCache() {
|
|
60
|
+
this.clearContext();
|
|
61
|
+
cache.removeItem(this.localStorageKey);
|
|
62
|
+
cache.removeItem(this.localStorageLastUpdatedKey);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clearContext() {
|
|
66
|
+
this.context = null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async update(localUpdateFunction, serverUpdateFunction) {
|
|
70
|
+
this.ensureLocalContextLoaded();
|
|
71
|
+
if (this.context) {
|
|
72
|
+
localUpdateFunction(this.context);
|
|
73
|
+
this.context.version++;
|
|
74
|
+
let data = JSON.stringify(this.context);
|
|
75
|
+
cache.setItem(this.localStorageKey, data);
|
|
76
|
+
cache.setItem(this.localStorageLastUpdatedKey, new Date().getTime());
|
|
77
|
+
}
|
|
78
|
+
let response = await serverUpdateFunction();
|
|
79
|
+
if (response.version !== this.version()) {
|
|
80
|
+
this.clearCache();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
version() {
|
|
85
|
+
return this.context?.version ?? -1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module Railcontent-Services
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const {globalConfig} = require('./config');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -24,10 +24,11 @@ export async function fetchCompletedState(content_id) {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
const response = await fetchAbsolute(url, {
|
|
27
|
+
const response = await fetchAbsolute(url, {headers});
|
|
28
28
|
const result = await response.json();
|
|
29
29
|
|
|
30
|
-
if (result && result[content_id]) {
|
|
30
|
+
if (result && result[content_id]) {
|
|
31
|
+
return result[content_id]; // Return the correct object
|
|
31
32
|
} else {
|
|
32
33
|
return null; // Handle unexpected structure
|
|
33
34
|
}
|
|
@@ -56,7 +57,7 @@ export async function fetchVimeoData(vimeo_id) {
|
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
|
-
const response = await fetchAbsolute(url, {
|
|
60
|
+
const response = await fetchAbsolute(url, {headers});
|
|
60
61
|
const result = await response.json();
|
|
61
62
|
|
|
62
63
|
if (result) {
|
|
@@ -91,9 +92,9 @@ export async function fetchAllCompletedStates(contentIds) {
|
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
try {
|
|
94
|
-
const response = await fetchAbsolute(url, {
|
|
95
|
+
const response = await fetchAbsolute(url, {headers});
|
|
95
96
|
const result = await response.json();
|
|
96
|
-
if(result){
|
|
97
|
+
if (result) {
|
|
97
98
|
return result;
|
|
98
99
|
} else {
|
|
99
100
|
console.log('result not json');
|
|
@@ -123,9 +124,9 @@ export async function fetchSongsInProgress(brand) {
|
|
|
123
124
|
};
|
|
124
125
|
|
|
125
126
|
try {
|
|
126
|
-
const response = await fetchAbsolute(url, {
|
|
127
|
+
const response = await fetchAbsolute(url, {headers});
|
|
127
128
|
const result = await response.json();
|
|
128
|
-
if(result){
|
|
129
|
+
if (result) {
|
|
129
130
|
//console.log('fetchSongsInProgress', result);
|
|
130
131
|
return result;
|
|
131
132
|
} else {
|
|
@@ -150,12 +151,12 @@ export async function fetchSongsInProgress(brand) {
|
|
|
150
151
|
* .then(songs => console.log(songs))
|
|
151
152
|
* .catch(error => console.error(error));
|
|
152
153
|
*/
|
|
153
|
-
export async function fetchContentInProgress(type="all", brand, {
|
|
154
|
+
export async function fetchContentInProgress(type = "all", brand, {page, limit} = {}) {
|
|
154
155
|
let url;
|
|
155
156
|
const limitString = limit ? `&limit=${limit}` : '';
|
|
156
157
|
const pageString = page ? `&page=${page}` : '';
|
|
157
158
|
|
|
158
|
-
if(type === "all") {
|
|
159
|
+
if (type === "all") {
|
|
159
160
|
url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?brand=${brand}${limitString}${pageString}`;
|
|
160
161
|
} else {
|
|
161
162
|
url = `/content/in_progress/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}${limitString}${pageString}`;
|
|
@@ -165,9 +166,9 @@ export async function fetchContentInProgress(type="all", brand, { page, limit }
|
|
|
165
166
|
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
166
167
|
};
|
|
167
168
|
try {
|
|
168
|
-
const response = await fetchAbsolute(url, {
|
|
169
|
+
const response = await fetchAbsolute(url, {headers});
|
|
169
170
|
const result = await response.json();
|
|
170
|
-
if(result){
|
|
171
|
+
if (result) {
|
|
171
172
|
//console.log('contentInProgress', result);
|
|
172
173
|
return result;
|
|
173
174
|
} else {
|
|
@@ -192,12 +193,12 @@ export async function fetchContentInProgress(type="all", brand, { page, limit }
|
|
|
192
193
|
* .then(songs => console.log(songs))
|
|
193
194
|
* .catch(error => console.error(error));
|
|
194
195
|
*/
|
|
195
|
-
export async function fetchCompletedContent(type="all", brand, {
|
|
196
|
+
export async function fetchCompletedContent(type = "all", brand, {page, limit} = {}) {
|
|
196
197
|
let url;
|
|
197
198
|
const limitString = limit ? `&limit=${limit}` : '';
|
|
198
199
|
const pageString = page ? `&page=${page}` : '';
|
|
199
200
|
|
|
200
|
-
if(type === "all") {
|
|
201
|
+
if (type === "all") {
|
|
201
202
|
url = `/content/completed/${globalConfig.railcontentConfig.userId}?brand=${brand}${limitString}${pageString}`;
|
|
202
203
|
} else {
|
|
203
204
|
url = `/content/completed/${globalConfig.railcontentConfig.userId}?content_type=${type}&brand=${brand}${limitString}${pageString}`;
|
|
@@ -207,9 +208,9 @@ export async function fetchCompletedContent(type="all", brand, { page, limit } =
|
|
|
207
208
|
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
208
209
|
};
|
|
209
210
|
try {
|
|
210
|
-
const response = await fetchAbsolute(url, {
|
|
211
|
+
const response = await fetchAbsolute(url, {headers});
|
|
211
212
|
const result = await response.json();
|
|
212
|
-
if(result){
|
|
213
|
+
if (result) {
|
|
213
214
|
//console.log('completed content', result);
|
|
214
215
|
return result;
|
|
215
216
|
} else {
|
|
@@ -239,32 +240,11 @@ export async function fetchContentPageUserData(contentId) {
|
|
|
239
240
|
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
240
241
|
};
|
|
241
242
|
|
|
242
|
-
try {
|
|
243
|
-
const response = await fetchAbsolute(url, { headers });
|
|
244
|
-
const result = await response.json();
|
|
245
|
-
if(result){
|
|
246
|
-
console.log('fetchContentPageUserData', result);
|
|
247
|
-
return result;
|
|
248
|
-
} else {
|
|
249
|
-
console.log('result not json');
|
|
250
|
-
}
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error('Fetch error:', error);
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export async function fetchUserContext() {
|
|
258
|
-
let url = `/content/user_data_all`;
|
|
259
|
-
const headers = {
|
|
260
|
-
'Content-Type': 'application/json',
|
|
261
|
-
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
262
|
-
};
|
|
263
243
|
try {
|
|
264
244
|
const response = await fetchAbsolute(url, {headers});
|
|
265
245
|
const result = await response.json();
|
|
266
246
|
if (result) {
|
|
267
|
-
console.log('
|
|
247
|
+
console.log('fetchContentPageUserData', result);
|
|
268
248
|
return result;
|
|
269
249
|
} else {
|
|
270
250
|
console.log('result not json');
|
|
@@ -287,11 +267,16 @@ export async function fetchUserPermissions() {
|
|
|
287
267
|
return fetchHandler(url, 'get') ?? [];
|
|
288
268
|
}
|
|
289
269
|
|
|
290
|
-
|
|
291
|
-
|
|
270
|
+
async function fetchDataHandler(url, dataVersion, method = "get") {
|
|
271
|
+
return fetchHandler(url, method, dataVersion);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export async function fetchHandler(url, method = "get", dataVersion = null) {
|
|
275
|
+
let headers = {
|
|
292
276
|
'Content-Type': 'application/json',
|
|
293
|
-
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token
|
|
277
|
+
'X-CSRF-TOKEN': globalConfig.railcontentConfig.token,
|
|
294
278
|
};
|
|
279
|
+
if (dataVersion) headers['Data-Version'] = dataVersion;
|
|
295
280
|
try {
|
|
296
281
|
const response = await fetchAbsolute(url, {method, headers});
|
|
297
282
|
const result = await response.json();
|
|
@@ -306,18 +291,23 @@ export async function fetchHandler(url, method = "get") {
|
|
|
306
291
|
return null;
|
|
307
292
|
}
|
|
308
293
|
|
|
309
|
-
export async function
|
|
310
|
-
let url = `/content
|
|
294
|
+
export async function fetchUserLikes(currentVersion) {
|
|
295
|
+
let url = `/content/user/likes/all`;
|
|
296
|
+
return fetchDataHandler(url, currentVersion);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export async function postContentLiked(contentId) {
|
|
300
|
+
let url = `/content/user/likes/like/${contentId}`;
|
|
311
301
|
return await fetchHandler(url, "post");
|
|
312
302
|
}
|
|
313
303
|
|
|
314
|
-
export async function
|
|
315
|
-
let url = `/content/${contentId}
|
|
304
|
+
export async function postContentUnliked(contentId) {
|
|
305
|
+
let url = `/content/user/likes/unlike/${contentId}`;
|
|
316
306
|
return await fetchHandler(url, "post");
|
|
317
307
|
}
|
|
318
308
|
|
|
319
309
|
function fetchAbsolute(url, params) {
|
|
320
|
-
if(globalConfig.railcontentConfig.baseUrl) {
|
|
310
|
+
if (globalConfig.railcontentConfig.baseUrl) {
|
|
321
311
|
if (url.startsWith('/')) {
|
|
322
312
|
return fetch(globalConfig.railcontentConfig.baseUrl + url, params)
|
|
323
313
|
}
|
package/src/services/sanity.js
CHANGED
|
@@ -873,7 +873,7 @@ export async function fetchLessonContent(railContentId) {
|
|
|
873
873
|
"thumbnail_url":thumbnail.asset->url,
|
|
874
874
|
"url": web_url_path,
|
|
875
875
|
soundslice_slug,
|
|
876
|
-
description,
|
|
876
|
+
"description": description[0].children[0].text,
|
|
877
877
|
"chapters": chapter[]{
|
|
878
878
|
chapter_description,
|
|
879
879
|
chapter_timecode,
|
|
@@ -889,6 +889,7 @@ export async function fetchLessonContent(railContentId) {
|
|
|
889
889
|
"id":_id,
|
|
890
890
|
name,
|
|
891
891
|
short_bio,
|
|
892
|
+
"biography": short_bio[0].children[0].text,
|
|
892
893
|
web_url_path,
|
|
893
894
|
"coach_card_image": coach_card_image.asset->url,
|
|
894
895
|
},
|
|
@@ -1072,6 +1073,8 @@ export async function fetchChallengeOverview(id) {
|
|
|
1072
1073
|
difficulty_string,
|
|
1073
1074
|
difficulty,
|
|
1074
1075
|
"type": _type,
|
|
1076
|
+
is_always_unlocked,
|
|
1077
|
+
is_bonus_content,
|
|
1075
1078
|
}
|
|
1076
1079
|
} [0...1]`;
|
|
1077
1080
|
return fetchSanity(query, false);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {isContentLiked, dataContext, likeContent, unlikeContent} from "../src/services/contentLikes";
|
|
2
|
+
import {LocalStorageMock} from "./localStorageMock";
|
|
3
|
+
import {initializeService} from "../src";
|
|
4
|
+
|
|
5
|
+
const railContentModule = require('../src/services/railcontent.js')
|
|
6
|
+
|
|
7
|
+
describe('commentLikesDataContext', function () {
|
|
8
|
+
let mock = null;
|
|
9
|
+
const testVersion = 1;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
initializeService({localStorage: new LocalStorageMock()});
|
|
13
|
+
mock = jest.spyOn(dataContext, 'fetchData');
|
|
14
|
+
var json = JSON.parse(`{"version":${testVersion},"data":[308516,308515,308514,308518]}`);
|
|
15
|
+
mock.mockImplementation(() => json);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('contentLiked', async () => {
|
|
19
|
+
let result = await isContentLiked(308516);
|
|
20
|
+
expect(result).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('contentNotLiked', async () => {
|
|
24
|
+
let result = await isContentLiked(121111);
|
|
25
|
+
expect(result).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('ensureOnlyOneServerFetchRequest', async () => {
|
|
29
|
+
dataContext.clearCache();
|
|
30
|
+
await isContentLiked(308516);
|
|
31
|
+
await isContentLiked(308514);
|
|
32
|
+
await isContentLiked(121111);
|
|
33
|
+
expect(dataContext.fetchData).toHaveBeenCalledTimes(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('ensureDataPulledFromLocalCache', async () => {
|
|
37
|
+
dataContext.clearCache();
|
|
38
|
+
await isContentLiked(308516);
|
|
39
|
+
dataContext.clearContext();
|
|
40
|
+
await isContentLiked(308514);
|
|
41
|
+
expect(dataContext.fetchData).toHaveBeenCalledTimes(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('likeContent', async () => {
|
|
45
|
+
mock = jest.spyOn(railContentModule, 'postContentLiked');
|
|
46
|
+
var json = JSON.parse(`{"version":${testVersion + 1}}`);
|
|
47
|
+
mock.mockImplementation(() => json);
|
|
48
|
+
|
|
49
|
+
dataContext.clearCache();
|
|
50
|
+
let isLiked = await isContentLiked(111111);
|
|
51
|
+
expect(isLiked).toBe(false);
|
|
52
|
+
|
|
53
|
+
await likeContent(111111);
|
|
54
|
+
isLiked = await isContentLiked(111111);
|
|
55
|
+
expect(isLiked).toBe(true);
|
|
56
|
+
|
|
57
|
+
dataContext.clearContext();
|
|
58
|
+
isLiked = await isContentLiked(111111);
|
|
59
|
+
expect(isLiked).toBe(true);
|
|
60
|
+
|
|
61
|
+
expect(dataContext.version()).toBe(testVersion + 1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
test('unlikeContent', async () => {
|
|
66
|
+
mock = jest.spyOn(railContentModule, 'postContentUnliked');
|
|
67
|
+
var json = JSON.parse(`{"version":${testVersion + 1}}`);
|
|
68
|
+
mock.mockImplementation(() => json);
|
|
69
|
+
|
|
70
|
+
dataContext.clearCache();
|
|
71
|
+
let isLiked = await isContentLiked(308516);
|
|
72
|
+
expect(isLiked).toBe(true);
|
|
73
|
+
|
|
74
|
+
await unlikeContent(308516);
|
|
75
|
+
console.log(dataContext.context);
|
|
76
|
+
isLiked = await isContentLiked(308516);
|
|
77
|
+
expect(isLiked).toBe(false);
|
|
78
|
+
|
|
79
|
+
dataContext.clearContext();
|
|
80
|
+
isLiked = await isContentLiked(308516);
|
|
81
|
+
expect(isLiked).toBe(false);
|
|
82
|
+
|
|
83
|
+
expect(dataContext.version()).toBe(testVersion + 1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
});
|
|
@@ -433,6 +433,16 @@ describe('Sanity Queries', function () {
|
|
|
433
433
|
expect(response.tabs.length).toBeGreaterThan(0);
|
|
434
434
|
});
|
|
435
435
|
|
|
436
|
+
test('fetchChallengesV2Fields', async () => {
|
|
437
|
+
const id = 402197;
|
|
438
|
+
const response = await fetchChallengeOverview(id);
|
|
439
|
+
log(response);
|
|
440
|
+
expect(response.award).toBeDefined();
|
|
441
|
+
expect(response.award_template).toBeDefined();
|
|
442
|
+
expect(response.lessons[0].is_always_unlocked).toBeDefined();
|
|
443
|
+
expect(response.lessons[0].is_bonus_content).toBeDefined();
|
|
444
|
+
});
|
|
445
|
+
|
|
436
446
|
});
|
|
437
447
|
|
|
438
448
|
describe('Filter Builder', function () {
|
|
@@ -618,6 +628,7 @@ describe('Filter Builder', function () {
|
|
|
618
628
|
});
|
|
619
629
|
});
|
|
620
630
|
|
|
631
|
+
|
|
621
632
|
describe('MetaData', function () {
|
|
622
633
|
|
|
623
634
|
test('customBrandTypeExists', async () => {
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import {fetchUserContext, fetchLikeContent, fetchUnlikeContent} from "./railcontent";
|
|
2
|
-
|
|
3
|
-
const StorageKey = "userContext";
|
|
4
|
-
let userContext = null;
|
|
5
|
-
let cache = null;
|
|
6
|
-
|
|
7
|
-
export function init(localCache) {
|
|
8
|
-
cache = localCache;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function version() {
|
|
12
|
-
ensureLocalContextLoaded();
|
|
13
|
-
return userContext.version;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function getUserContext() {
|
|
17
|
-
ensureLocalContextLoaded();
|
|
18
|
-
if (userContext) {
|
|
19
|
-
verifyContextIsValid();
|
|
20
|
-
}
|
|
21
|
-
if (!userContext) {
|
|
22
|
-
await fetchFromServer();
|
|
23
|
-
}
|
|
24
|
-
return userContext;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function verifyContextIsValid() {
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function ensureLocalContextLoaded() {
|
|
32
|
-
if (userContext) return;
|
|
33
|
-
let localData = cache.getItem(StorageKey);
|
|
34
|
-
if (localData) {
|
|
35
|
-
userContext = JSON.parse(localData);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function updateLocalContext(contentId, updateFunction) {
|
|
40
|
-
ensureLocalContextLoaded();
|
|
41
|
-
if (userContext) {
|
|
42
|
-
let contentData = userContext.data[contentId] ?? [];
|
|
43
|
-
updateFunction(contentData);
|
|
44
|
-
userContext.data[contentId] = contentData;
|
|
45
|
-
userContext.version++;
|
|
46
|
-
let data = JSON.stringify(userContext);
|
|
47
|
-
cache.setItem(StorageKey, data);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function fetchFromServer() {
|
|
52
|
-
let data = await fetchUserContext();
|
|
53
|
-
userContext = JSON.parse(data);
|
|
54
|
-
cache.setItem(StorageKey, data);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function transformData(data, contentId) {
|
|
59
|
-
let transformed = [];
|
|
60
|
-
transformed["contentId"] = contentId;
|
|
61
|
-
transformed["liked"] = (data && data.l) ?? 0;
|
|
62
|
-
return transformed;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function fetchContentData(contentId) {
|
|
66
|
-
let userContext = await getUserContext();
|
|
67
|
-
let data = userContext.data[contentId];
|
|
68
|
-
data = transformData(data, contentId);
|
|
69
|
-
return data;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function clearCache() {
|
|
73
|
-
userContext = null;
|
|
74
|
-
cache.setItem(StorageKey, null);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function testClearLocal() {
|
|
78
|
-
userContext = null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export async function likeContent(contentId) {
|
|
82
|
-
updateLocalContext(contentId,
|
|
83
|
-
function (contentData) {
|
|
84
|
-
contentData.l = 1;
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
let result = await fetchLikeContent(contentId);
|
|
89
|
-
if (result.version !== userContext.version) {
|
|
90
|
-
clearCache();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function unlikeContent(contentId) {
|
|
95
|
-
updateLocalContext(contentId,
|
|
96
|
-
function (contentData) {
|
|
97
|
-
contentData.l = 0;
|
|
98
|
-
}
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
let result = await fetchUnlikeContent(contentId);
|
|
102
|
-
if (result.version !== userContext.version) {
|
|
103
|
-
clearCache();
|
|
104
|
-
}
|
|
105
|
-
}
|
package/test/userContext.test.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const railContentModule = require('../src/services/railcontent.js')
|
|
2
|
-
const userContextModule = require('../src/services/userContext.js');
|
|
3
|
-
import {LocalStorageMock} from "./localStorageMock";
|
|
4
|
-
|
|
5
|
-
describe('userContext', function () {
|
|
6
|
-
let mock = null;
|
|
7
|
-
const testVersion = 1;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
userContextModule.init(new LocalStorageMock());
|
|
10
|
-
mock = jest.spyOn(railContentModule, 'fetchUserContext');
|
|
11
|
-
var json = `{"version":${testVersion},"data":{"308516":{"l":1},"308515":{"p":100},"308514":{"p":13},"308518":{"p":100}}}`;
|
|
12
|
-
mock.mockImplementation(() => json);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test('contentLiked', async () => {
|
|
16
|
-
let contentData = await userContextModule.fetchContentData(308516);
|
|
17
|
-
expect(contentData.liked).toBe(1);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('contentDoesNotExist', async () => {
|
|
21
|
-
//fetch content that does not exist
|
|
22
|
-
let contentData = await userContextModule.fetchContentData(121111);
|
|
23
|
-
expect(contentData.liked).toBe(0);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('ensureOnlyOneServerFetchRequest', async () => {
|
|
27
|
-
userContextModule.clearCache();
|
|
28
|
-
await userContextModule.fetchContentData(308516);
|
|
29
|
-
await userContextModule.fetchContentData(308514);
|
|
30
|
-
expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(1);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('ensureDataPulledFromLocalCache', async () => {
|
|
34
|
-
userContextModule.clearCache();
|
|
35
|
-
await userContextModule.fetchContentData(308516);
|
|
36
|
-
userContextModule.testClearLocal();
|
|
37
|
-
await userContextModule.fetchContentData(308514);
|
|
38
|
-
expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(1);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// test('hashExpiration', async () => {
|
|
42
|
-
// userContextModule.clearCache();
|
|
43
|
-
// await userContextModule.fetchContentData(testHash308516);
|
|
44
|
-
// let newHash = "8g9qg5wn3e5s5oi69q6g22et9w6g34t5";
|
|
45
|
-
// await userContextModule.fetchContentData(newHash, 308516);
|
|
46
|
-
// expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(2);
|
|
47
|
-
// });
|
|
48
|
-
|
|
49
|
-
test('likeContent', async () => {
|
|
50
|
-
mock = jest.spyOn(railContentModule, 'fetchLikeContent');
|
|
51
|
-
var json = JSON.parse(`{"version":${testVersion + 1}}`);
|
|
52
|
-
mock.mockImplementation(() => json);
|
|
53
|
-
|
|
54
|
-
userContextModule.clearCache();
|
|
55
|
-
await userContextModule.fetchContentData(308515);
|
|
56
|
-
await userContextModule.likeContent(308515);
|
|
57
|
-
let contentData = await userContextModule.fetchContentData(308515);
|
|
58
|
-
expect(contentData.liked).toBe(1);
|
|
59
|
-
|
|
60
|
-
userContextModule.testClearLocal();
|
|
61
|
-
contentData = await userContextModule.fetchContentData(308515);
|
|
62
|
-
expect(contentData.liked).toBe(1);
|
|
63
|
-
|
|
64
|
-
expect(userContextModule.version()).toBe(testVersion + 1);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
test('unlikeContent', async () => {
|
|
69
|
-
mock = jest.spyOn(railContentModule, 'fetchUnlikeContent');
|
|
70
|
-
var json = JSON.parse(`{"version":${testVersion + 1}}`);
|
|
71
|
-
mock.mockImplementation(() => json);
|
|
72
|
-
|
|
73
|
-
userContextModule.clearCache();
|
|
74
|
-
await userContextModule.fetchContentData(308516);
|
|
75
|
-
await userContextModule.unlikeContent(308516);
|
|
76
|
-
let contentData = await userContextModule.fetchContentData(308516);
|
|
77
|
-
expect(contentData.liked).toBe(0);
|
|
78
|
-
|
|
79
|
-
userContextModule.testClearLocal();
|
|
80
|
-
contentData = await userContextModule.fetchContentData(308516);
|
|
81
|
-
expect(contentData.liked).toBe(0);
|
|
82
|
-
|
|
83
|
-
expect(userContextModule.version()).toBe(testVersion + 1);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
});
|