musora-content-services 2.31.2 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.coderabbit.yaml +5 -0
- package/CHANGELOG.md +27 -0
- package/docs/ContentOrganization.html +2 -2
- package/docs/Gamification.html +2 -2
- package/docs/UserManagementSystem.html +2 -2
- package/docs/api_types.js.html +2 -2
- package/docs/config.js.html +2 -8
- package/docs/content-org_content-org.js.html +2 -2
- package/docs/content-org_guided-courses.ts.html +118 -0
- package/docs/content-org_playlists-types.js.html +12 -2
- package/docs/content-org_playlists.js.html +16 -16
- package/docs/content.js.html +71 -39
- package/docs/gamification_awards.js.html +54 -35
- package/docs/gamification_awards.ts.html +160 -0
- package/docs/gamification_gamification.js.html +2 -2
- package/docs/gamification_types.js.html +7 -25
- package/docs/global.html +250 -494
- package/docs/index.html +2 -2
- package/docs/module-Accounts.html +108 -0
- package/docs/module-Awards.html +196 -11
- package/docs/module-Config.html +4 -8
- package/docs/module-Content-Services-V2.html +455 -8
- package/docs/module-GuidedCourses.html +108 -0
- package/docs/module-Interests.html +2 -2
- package/docs/module-Permissions.html +2 -2
- package/docs/module-Playlists.html +18 -17
- package/docs/module-Railcontent-Services.html +479 -35
- package/docs/module-Sanity-Services.html +45 -173
- package/docs/module-Sessions.html +2 -2
- package/docs/module-UserActivity.html +189 -21
- package/docs/module-UserChat.html +2 -2
- package/docs/module-UserManagement.html +410 -8
- package/docs/module-UserNotifications.html +203 -8
- package/docs/module-UserProfile.html +2 -2
- package/docs/railcontent.js.html +65 -37
- package/docs/sanity.js.html +120 -185
- package/docs/userActivity.js.html +448 -466
- package/docs/user_account.ts.html +138 -0
- package/docs/user_chat.js.html +2 -2
- package/docs/user_interests.js.html +2 -2
- package/docs/user_management.js.html +33 -2
- package/docs/user_notifications.js.html +27 -5
- package/docs/user_permissions.js.html +2 -2
- package/docs/user_profile.js.html +3 -9
- package/docs/user_sessions.js.html +2 -2
- package/docs/user_types.js.html +10 -2
- package/docs/user_user-management-system.js.html +2 -2
- package/jsdoc.json +11 -1
- package/package.json +5 -1
- package/src/contentTypeConfig.js +6 -1
- package/src/index.d.ts +14 -2
- package/src/index.js +14 -2
- package/src/services/api/types.ts +22 -0
- package/src/services/gamification/awards.ts +86 -0
- package/src/services/sanity.js +14 -19
- package/src/services/user/account.ts +24 -0
- package/src/services/user/management.js +31 -0
- package/src/services/user/types.js +8 -0
- package/src/services/userActivity.js +5 -5
- package/src/services/gamification/awards.js +0 -93
- package/src/services/gamification/types.js +0 -8
package/jsdoc.json
CHANGED
|
@@ -12,9 +12,19 @@
|
|
|
12
12
|
"src/services/railcontent.js",
|
|
13
13
|
"src/index.js"
|
|
14
14
|
],
|
|
15
|
-
"includePattern": ".js$",
|
|
15
|
+
"includePattern": "(.js|.ts$)",
|
|
16
16
|
"excludePattern": "(node_modules/|docs)"
|
|
17
17
|
},
|
|
18
|
+
"plugins": [
|
|
19
|
+
"node_modules/jsdoc-babel"
|
|
20
|
+
],
|
|
21
|
+
"babel": {
|
|
22
|
+
"extensions": ["ts"],
|
|
23
|
+
"ignore": ["**/*.(test|spec).ts"],
|
|
24
|
+
"babelrc": false,
|
|
25
|
+
"presets": [["@babel/preset-env", { "targets": { "node": true } }], "@babel/preset-typescript"],
|
|
26
|
+
"plugins": ["@babel/plugin-transform-class-properties", "@babel/plugin-transform-object-rest-spread"]
|
|
27
|
+
},
|
|
18
28
|
"opts": {
|
|
19
29
|
"destination": "./docs",
|
|
20
30
|
"recurse": true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musora-content-services",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.33.0",
|
|
4
4
|
"description": "A package for Musoras content services ",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,9 +21,13 @@
|
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://github.com/railroadmedia/musora-content-services#readme",
|
|
23
23
|
"devDependencies": {
|
|
24
|
+
"@babel/plugin-transform-class-properties": "^7.27.1",
|
|
25
|
+
"@babel/plugin-transform-object-rest-spread": "^7.28.0",
|
|
26
|
+
"@babel/preset-typescript": "^7.27.1",
|
|
24
27
|
"dotenv": "^16.4.5",
|
|
25
28
|
"jest": "^29.7.0",
|
|
26
29
|
"jsdoc": "^4.0.3",
|
|
30
|
+
"jsdoc-babel": "^0.5.0",
|
|
27
31
|
"prettier": "3.4.2",
|
|
28
32
|
"standard-version": "^9.5.0",
|
|
29
33
|
"ts-jest": "^29.2.4"
|
package/src/contentTypeConfig.js
CHANGED
|
@@ -9,12 +9,17 @@ export const SONG_TYPES = ['song', 'play-along', 'jam-track', 'song-tutorial-chi
|
|
|
9
9
|
// Single hierarchy refers to only one element in the hierarchy has video lessons, not that they have a single parent
|
|
10
10
|
export const SINGLE_PARENT_TYPES = ['course-part', 'pack-bundle-lesson', 'song-tutorial-children']
|
|
11
11
|
|
|
12
|
+
export const artistField = `select(
|
|
13
|
+
defined(artist) => artist->{ 'name': name, 'thumbnail': thumbnail_url.asset->url},
|
|
14
|
+
defined(parent_content_data) => *[_type == ^.parent_content_data[0].type && railcontent_id == ^.parent_content_data[0].id][0].artist->{ 'name': name, 'thumbnail': thumbnail_url.asset->url}
|
|
15
|
+
)`
|
|
16
|
+
|
|
12
17
|
export const DEFAULT_FIELDS = [
|
|
13
18
|
"'sanity_id' : _id",
|
|
14
19
|
"'id': railcontent_id",
|
|
15
20
|
'railcontent_id',
|
|
16
21
|
artistOrInstructorName(),
|
|
17
|
-
|
|
22
|
+
`'artist': ${artistField}`,
|
|
18
23
|
'title',
|
|
19
24
|
"'image': thumbnail.asset->url",
|
|
20
25
|
"'thumbnail': thumbnail.asset->url",
|
package/src/index.d.ts
CHANGED
|
@@ -95,8 +95,9 @@ import {
|
|
|
95
95
|
} from './services/forum.js';
|
|
96
96
|
|
|
97
97
|
import {
|
|
98
|
-
fetchAwardsForUser
|
|
99
|
-
|
|
98
|
+
fetchAwardsForUser,
|
|
99
|
+
fetchCertificate
|
|
100
|
+
} from './services/gamification/awards.ts';
|
|
100
101
|
|
|
101
102
|
import {
|
|
102
103
|
applyCloudflareWrapper,
|
|
@@ -219,6 +220,8 @@ import {
|
|
|
219
220
|
} from './services/sanity.js';
|
|
220
221
|
|
|
221
222
|
import {
|
|
223
|
+
confirmEmailChange,
|
|
224
|
+
requestEmailChange,
|
|
222
225
|
resetPassword,
|
|
223
226
|
sendAccountSetupEmail,
|
|
224
227
|
sendPasswordResetEmail,
|
|
@@ -241,9 +244,12 @@ import {
|
|
|
241
244
|
|
|
242
245
|
import {
|
|
243
246
|
blockUser,
|
|
247
|
+
blockedUsers,
|
|
244
248
|
deletePicture,
|
|
245
249
|
getUserData,
|
|
250
|
+
isDisplayNameAvailable,
|
|
246
251
|
unblockUser,
|
|
252
|
+
updateDisplayName,
|
|
247
253
|
uploadPicture,
|
|
248
254
|
uploadPictureFromS3
|
|
249
255
|
} from './services/user/management.js';
|
|
@@ -312,9 +318,11 @@ declare module 'musora-content-services' {
|
|
|
312
318
|
applySanityTransformations,
|
|
313
319
|
assignModeratorToComment,
|
|
314
320
|
blockUser,
|
|
321
|
+
blockedUsers,
|
|
315
322
|
buildImageSRC,
|
|
316
323
|
calculateLongestStreaks,
|
|
317
324
|
closeComment,
|
|
325
|
+
confirmEmailChange,
|
|
318
326
|
contentStatusCompleted,
|
|
319
327
|
contentStatusReset,
|
|
320
328
|
convertToTimeZone,
|
|
@@ -344,6 +352,7 @@ declare module 'musora-content-services' {
|
|
|
344
352
|
fetchByRailContentIds,
|
|
345
353
|
fetchByReference,
|
|
346
354
|
fetchCarouselCardData,
|
|
355
|
+
fetchCertificate,
|
|
347
356
|
fetchChatAndLiveEnvent,
|
|
348
357
|
fetchChatSettings,
|
|
349
358
|
fetchCoachLessons,
|
|
@@ -463,6 +472,7 @@ declare module 'musora-content-services' {
|
|
|
463
472
|
isBucketUrl,
|
|
464
473
|
isContentLiked,
|
|
465
474
|
isContentLikedByIds,
|
|
475
|
+
isDisplayNameAvailable,
|
|
466
476
|
isNextDay,
|
|
467
477
|
isSameDate,
|
|
468
478
|
jumpToContinueContent,
|
|
@@ -502,6 +512,7 @@ declare module 'musora-content-services' {
|
|
|
502
512
|
replyToComment,
|
|
503
513
|
reportComment,
|
|
504
514
|
reportPlaylist,
|
|
515
|
+
requestEmailChange,
|
|
505
516
|
reset,
|
|
506
517
|
resetPassword,
|
|
507
518
|
restoreComment,
|
|
@@ -527,6 +538,7 @@ declare module 'musora-content-services' {
|
|
|
527
538
|
unlikeContent,
|
|
528
539
|
unlikePlaylist,
|
|
529
540
|
unpinProgressRow,
|
|
541
|
+
updateDisplayName,
|
|
530
542
|
updateNotificationSetting,
|
|
531
543
|
updatePlaylist,
|
|
532
544
|
updatePracticeNotes,
|
package/src/index.js
CHANGED
|
@@ -95,8 +95,9 @@ import {
|
|
|
95
95
|
} from './services/forum.js';
|
|
96
96
|
|
|
97
97
|
import {
|
|
98
|
-
fetchAwardsForUser
|
|
99
|
-
|
|
98
|
+
fetchAwardsForUser,
|
|
99
|
+
fetchCertificate
|
|
100
|
+
} from './services/gamification/awards.ts';
|
|
100
101
|
|
|
101
102
|
import {
|
|
102
103
|
applyCloudflareWrapper,
|
|
@@ -219,6 +220,8 @@ import {
|
|
|
219
220
|
} from './services/sanity.js';
|
|
220
221
|
|
|
221
222
|
import {
|
|
223
|
+
confirmEmailChange,
|
|
224
|
+
requestEmailChange,
|
|
222
225
|
resetPassword,
|
|
223
226
|
sendAccountSetupEmail,
|
|
224
227
|
sendPasswordResetEmail,
|
|
@@ -241,9 +244,12 @@ import {
|
|
|
241
244
|
|
|
242
245
|
import {
|
|
243
246
|
blockUser,
|
|
247
|
+
blockedUsers,
|
|
244
248
|
deletePicture,
|
|
245
249
|
getUserData,
|
|
250
|
+
isDisplayNameAvailable,
|
|
246
251
|
unblockUser,
|
|
252
|
+
updateDisplayName,
|
|
247
253
|
uploadPicture,
|
|
248
254
|
uploadPictureFromS3
|
|
249
255
|
} from './services/user/management.js';
|
|
@@ -311,9 +317,11 @@ export {
|
|
|
311
317
|
applySanityTransformations,
|
|
312
318
|
assignModeratorToComment,
|
|
313
319
|
blockUser,
|
|
320
|
+
blockedUsers,
|
|
314
321
|
buildImageSRC,
|
|
315
322
|
calculateLongestStreaks,
|
|
316
323
|
closeComment,
|
|
324
|
+
confirmEmailChange,
|
|
317
325
|
contentStatusCompleted,
|
|
318
326
|
contentStatusReset,
|
|
319
327
|
convertToTimeZone,
|
|
@@ -343,6 +351,7 @@ export {
|
|
|
343
351
|
fetchByRailContentIds,
|
|
344
352
|
fetchByReference,
|
|
345
353
|
fetchCarouselCardData,
|
|
354
|
+
fetchCertificate,
|
|
346
355
|
fetchChatAndLiveEnvent,
|
|
347
356
|
fetchChatSettings,
|
|
348
357
|
fetchCoachLessons,
|
|
@@ -462,6 +471,7 @@ export {
|
|
|
462
471
|
isBucketUrl,
|
|
463
472
|
isContentLiked,
|
|
464
473
|
isContentLikedByIds,
|
|
474
|
+
isDisplayNameAvailable,
|
|
465
475
|
isNextDay,
|
|
466
476
|
isSameDate,
|
|
467
477
|
jumpToContinueContent,
|
|
@@ -501,6 +511,7 @@ export {
|
|
|
501
511
|
replyToComment,
|
|
502
512
|
reportComment,
|
|
503
513
|
reportPlaylist,
|
|
514
|
+
requestEmailChange,
|
|
504
515
|
reset,
|
|
505
516
|
resetPassword,
|
|
506
517
|
restoreComment,
|
|
@@ -526,6 +537,7 @@ export {
|
|
|
526
537
|
unlikeContent,
|
|
527
538
|
unlikePlaylist,
|
|
528
539
|
unpinProgressRow,
|
|
540
|
+
updateDisplayName,
|
|
529
541
|
updateNotificationSetting,
|
|
530
542
|
updatePlaylist,
|
|
531
543
|
updatePracticeNotes,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PaginatedMeta {
|
|
2
|
+
current_page: number
|
|
3
|
+
from: number
|
|
4
|
+
to: number
|
|
5
|
+
per_page: number
|
|
6
|
+
last_page: number
|
|
7
|
+
total: number
|
|
8
|
+
path: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PaginatedLinks {
|
|
12
|
+
first: string
|
|
13
|
+
last: string
|
|
14
|
+
next: string
|
|
15
|
+
prev: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PaginatedResponse<T> {
|
|
19
|
+
meta: PaginatedMeta
|
|
20
|
+
links: PaginatedLinks
|
|
21
|
+
data: Array<T>
|
|
22
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Awards
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { HttpClient } from '../../infrastructure/http/HttpClient'
|
|
6
|
+
import { PaginatedResponse } from '../api/types'
|
|
7
|
+
import { globalConfig } from '../config'
|
|
8
|
+
|
|
9
|
+
const baseUrl = `/api/gamification`
|
|
10
|
+
|
|
11
|
+
export interface Award {
|
|
12
|
+
id: number
|
|
13
|
+
user_id: number
|
|
14
|
+
completed_at: string // ISO-8601 timestamp
|
|
15
|
+
completion_data: Object
|
|
16
|
+
award_id: number
|
|
17
|
+
type: string
|
|
18
|
+
title: string
|
|
19
|
+
badge: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Certificate {
|
|
23
|
+
id: number
|
|
24
|
+
user_name: string
|
|
25
|
+
user_id: number
|
|
26
|
+
completed_at: string // ISO-8601 timestamp
|
|
27
|
+
message: string
|
|
28
|
+
award_id: number
|
|
29
|
+
type: string
|
|
30
|
+
title: string
|
|
31
|
+
musora_logo: string
|
|
32
|
+
musora_logo_64: string
|
|
33
|
+
brand_logo: string
|
|
34
|
+
brand_logo_64: string
|
|
35
|
+
ribbon_image: string
|
|
36
|
+
ribbon_image_64: string
|
|
37
|
+
award_image: string
|
|
38
|
+
award_image_64: string
|
|
39
|
+
|
|
40
|
+
instructor_signature?: string
|
|
41
|
+
instructor_signature_64?: string
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get awards for a specific user.
|
|
45
|
+
*
|
|
46
|
+
* NOTE: needs error handling for the response from http client
|
|
47
|
+
* (Alexandre: I'm doing it in a different branch/PR: https://github.com/railroadmedia/musora-content-services/pull/349)
|
|
48
|
+
* NOTE: This function still expects brand because FE passes the argument. It is ignored for now
|
|
49
|
+
*
|
|
50
|
+
* @param {number} userId - The user ID. If not provided, the authenticated user is used instead.
|
|
51
|
+
* @param {string} _brand - The brand to fetch the awards for.
|
|
52
|
+
* @param {number|null} [page=1] - Page attribute for pagination
|
|
53
|
+
* @param {number|null} [limit=5] - Limit how many items to return
|
|
54
|
+
* @returns {Promise<PaginatedResponse<Award>>} - The awards for the user.
|
|
55
|
+
*/
|
|
56
|
+
export async function fetchAwardsForUser(
|
|
57
|
+
userId: number,
|
|
58
|
+
_brand: string,
|
|
59
|
+
page: number = 1,
|
|
60
|
+
limit: number = 5
|
|
61
|
+
): Promise<PaginatedResponse<Award>> {
|
|
62
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
63
|
+
const response = await httpClient.get<PaginatedResponse<Award>>(
|
|
64
|
+
`${baseUrl}/v1/users/${userId}/awards?limit=${limit}&page=${page}`
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return response
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get certificate data for a completed user award
|
|
72
|
+
*
|
|
73
|
+
* NOTE: needs error handling for the response from http client
|
|
74
|
+
* (Alexandre: I'm doing it in a different branch/PR: https://github.com/railroadmedia/musora-content-services/pull/349)
|
|
75
|
+
* NOTE: This function still expects brand because FE passes the argument. It is ignored for now
|
|
76
|
+
*
|
|
77
|
+
* @param {number} userAwardId - The user award progress id
|
|
78
|
+
* @returns {Promise<Certificate>} - The certificate data for the completed user award.
|
|
79
|
+
*/
|
|
80
|
+
export async function fetchCertificate(userAwardId: number): Promise<Certificate> {
|
|
81
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
82
|
+
const response = await httpClient.get<Certificate>(
|
|
83
|
+
`${baseUrl}/v1/users/certificate/${userAwardId}`
|
|
84
|
+
)
|
|
85
|
+
return response
|
|
86
|
+
}
|
package/src/services/sanity.js
CHANGED
|
@@ -1298,33 +1298,28 @@ export async function fetchSiblingContent(railContentId, brand= null)
|
|
|
1298
1298
|
/**
|
|
1299
1299
|
* Fetch lessons related to a specific lesson by RailContent ID and type.
|
|
1300
1300
|
* @param {string} railContentId - The RailContent ID of the current lesson.
|
|
1301
|
-
* @param {string} brand - The current brand.
|
|
1302
1301
|
* @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
|
|
1303
1302
|
*/
|
|
1304
|
-
export async function fetchRelatedLessons(railContentId
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
).buildFilter()
|
|
1311
|
-
const filterSongSameArtist = await new FilterBuilder(
|
|
1312
|
-
`_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`
|
|
1303
|
+
export async function fetchRelatedLessons(railContentId)
|
|
1304
|
+
{
|
|
1305
|
+
const defaultFilterFields = `_type==^._type && brand == ^.brand && railcontent_id != ${railContentId}`
|
|
1306
|
+
|
|
1307
|
+
const filterSameArtist = await new FilterBuilder(
|
|
1308
|
+
`${defaultFilterFields} && references(^.artist->_id)`
|
|
1313
1309
|
).buildFilter()
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1310
|
+
const filterSameGenre = await new FilterBuilder(
|
|
1311
|
+
`${defaultFilterFields} && references(^.genre[]->_id)`
|
|
1316
1312
|
).buildFilter()
|
|
1313
|
+
|
|
1317
1314
|
const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, "genre": genre[]->name`
|
|
1318
|
-
|
|
1319
|
-
const query = `*[railcontent_id == ${railContentId} &&
|
|
1315
|
+
|
|
1316
|
+
const query = `*[railcontent_id == ${railContentId} && (!defined(permission) || references(*[_type=='permission']._id))]{
|
|
1320
1317
|
_type, parent_type, railcontent_id,
|
|
1321
1318
|
"related_lessons" : array::unique([
|
|
1322
|
-
...(*[${
|
|
1323
|
-
...(*[${
|
|
1324
|
-
...(*[${filterSameTypeAndSortOrder}]{${queryFieldsWithSort}}|order(sort asc, title asc)[0...10]),
|
|
1325
|
-
...(*[${filterSameType}]{${queryFields}}|order(published_on desc, title asc)[0...10])
|
|
1326
|
-
,
|
|
1319
|
+
...(*[${filterSameArtist}]{${queryFields}}|order(published_on desc, title asc)[0...10]),
|
|
1320
|
+
...(*[${filterSameGenre}]{${queryFields}}|order(published_on desc, title asc)[0...10]),
|
|
1327
1321
|
])[0...10]}`
|
|
1322
|
+
|
|
1328
1323
|
return await fetchSanity(query, false)
|
|
1329
1324
|
}
|
|
1330
1325
|
|
|
@@ -64,3 +64,27 @@ export async function resetPassword({
|
|
|
64
64
|
token,
|
|
65
65
|
})
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} email - The new email address to set for the user.
|
|
70
|
+
* @param {string} password - The current password of the user for verification.
|
|
71
|
+
* @returns {Promise<void|HttpError>} - A promise that resolves when the email change request is made.
|
|
72
|
+
*/
|
|
73
|
+
export async function requestEmailChange(
|
|
74
|
+
email: string,
|
|
75
|
+
password: string
|
|
76
|
+
): Promise<void | HttpError> {
|
|
77
|
+
const apiUrl = `/api/user-management-system/v1/accounts/${globalConfig.sessionConfig.userId}/email-change`
|
|
78
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
79
|
+
return httpClient.post(apiUrl, { email, password })
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} token - The token sent to the user's email for verification.
|
|
84
|
+
* @returns {Promise<void|HttpError>} - A promise that resolves when the email change is confirmed.
|
|
85
|
+
*/
|
|
86
|
+
export async function confirmEmailChange(token: string): Promise<void | HttpError> {
|
|
87
|
+
const apiUrl = `/api/user-management-system/v1/accounts/email-change/confirm`
|
|
88
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
89
|
+
return httpClient.post(apiUrl, { token })
|
|
90
|
+
}
|
|
@@ -9,6 +9,16 @@ import { HttpClient } from '../../infrastructure/http/HttpClient'
|
|
|
9
9
|
|
|
10
10
|
const baseUrl = `/api/user-management-system`
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Fetches the blocked users for the current user.
|
|
14
|
+
* @returns {Promise<Array<BlockedUsersDTO>>}
|
|
15
|
+
*/
|
|
16
|
+
export async function blockedUsers() {
|
|
17
|
+
const url = `${baseUrl}/v1/users/${globalConfig.sessionConfig.userId}/blocked`
|
|
18
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
19
|
+
return httpClient.get(url)
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* Block the provided user
|
|
14
24
|
* @param userId
|
|
@@ -116,3 +126,24 @@ export async function getUserData(userId = globalConfig.sessionConfig.userId) {
|
|
|
116
126
|
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
117
127
|
return httpClient.get(apiUrl)
|
|
118
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param displayName - The display name to check for availability.
|
|
132
|
+
* @returns {Promise<{ available: boolean }>} - An object indicating if the display name is available.
|
|
133
|
+
*/
|
|
134
|
+
export async function isDisplayNameAvailable(displayName) {
|
|
135
|
+
const apiUrl = `${baseUrl}/v1/users/display-names/available?display_name=${encodeURIComponent(displayName)}`
|
|
136
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
137
|
+
return httpClient.get(apiUrl)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param newDisplayName - The new display name to set for the user.
|
|
142
|
+
* @returns {Promise<User>} - A promise that resolves when the display name is updated.
|
|
143
|
+
*/
|
|
144
|
+
export async function updateDisplayName(newDisplayName) {
|
|
145
|
+
const apiUrl = `${baseUrl}/v1/users/${globalConfig.sessionConfig.userId}/display-name`
|
|
146
|
+
const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
|
|
147
|
+
return httpClient.put(apiUrl, { display_name: newDisplayName })
|
|
148
|
+
}
|
|
149
|
+
|
|
@@ -134,3 +134,11 @@
|
|
|
134
134
|
* @property {number} forum_post_likes
|
|
135
135
|
* @property {number} experience_points
|
|
136
136
|
*/
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @typedef {Object} BlockedUsersDTO
|
|
140
|
+
*
|
|
141
|
+
* @property {number} id
|
|
142
|
+
* @property {string} display_name
|
|
143
|
+
* @property {string|null} profile_picture_url
|
|
144
|
+
*/
|
|
@@ -995,6 +995,7 @@ function generateContentsMap(contents, playlistsContents) {
|
|
|
995
995
|
const existingShows = new Set()
|
|
996
996
|
const contentsMap = new Map()
|
|
997
997
|
const childToParentMap = {}
|
|
998
|
+
if (!contents) return contentsMap
|
|
998
999
|
contents.forEach((content) => {
|
|
999
1000
|
if (Array.isArray(content.parent_content_data) && content.parent_content_data.length > 0) {
|
|
1000
1001
|
childToParentMap[content.id] =
|
|
@@ -1073,20 +1074,20 @@ export async function getProgressRows({ brand = null, limit = 8 } = {}) {
|
|
|
1073
1074
|
nonPlaylistContentIds.push(userPinnedItem.id)
|
|
1074
1075
|
}
|
|
1075
1076
|
const [playlistsContents, contents] = await Promise.all([
|
|
1076
|
-
addContextToContent(fetchByRailContentIds, playlistEngagedOnContents, 'progress-tracker', {
|
|
1077
|
+
playlistEngagedOnContents ? addContextToContent(fetchByRailContentIds, playlistEngagedOnContents, 'progress-tracker', {
|
|
1077
1078
|
addNextLesson: true,
|
|
1078
1079
|
addNavigateTo: true,
|
|
1079
1080
|
addProgressStatus: true,
|
|
1080
1081
|
addProgressPercentage: true,
|
|
1081
1082
|
addProgressTimestamp: true,
|
|
1082
|
-
}),
|
|
1083
|
-
addContextToContent(fetchByRailContentIds, nonPlaylistContentIds, 'progress-tracker', brand, {
|
|
1083
|
+
}) : Promise.resolve([]),
|
|
1084
|
+
nonPlaylistContentIds ? addContextToContent(fetchByRailContentIds, nonPlaylistContentIds, 'progress-tracker', brand, {
|
|
1084
1085
|
addNextLesson: true,
|
|
1085
1086
|
addNavigateTo: true,
|
|
1086
1087
|
addProgressStatus: true,
|
|
1087
1088
|
addProgressPercentage: true,
|
|
1088
1089
|
addProgressTimestamp: true,
|
|
1089
|
-
}),
|
|
1090
|
+
}) : Promise.resolve([]),
|
|
1090
1091
|
])
|
|
1091
1092
|
const contentsMap = generateContentsMap(contents, playlistsContents)
|
|
1092
1093
|
let combined = await extractPinnedItemsAndSortAllItems(
|
|
@@ -1103,7 +1104,6 @@ export async function getProgressRows({ brand = null, limit = 8 } = {}) {
|
|
|
1103
1104
|
item.type === 'playlist' ? processPlaylistItem(item) : processContentItem(item)
|
|
1104
1105
|
)
|
|
1105
1106
|
)
|
|
1106
|
-
console.log('HomePageProgressRows results: remove before merge', results)
|
|
1107
1107
|
return {
|
|
1108
1108
|
type: TabResponseType.PROGRESS_ROWS,
|
|
1109
1109
|
displayBrowseAll: combined.length > limit,
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module Awards
|
|
3
|
-
*/
|
|
4
|
-
import '../api/types.js'
|
|
5
|
-
import './types.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get awards for a specific user.
|
|
9
|
-
*
|
|
10
|
-
* @param {number|null} userId - The user ID. If not provided, the authenticated user is used instead.
|
|
11
|
-
* @param {string|null} brand - Brand
|
|
12
|
-
* @param {number|null} page - Page attribute for pagination
|
|
13
|
-
* @param {number|null} limit - Limit how many items to return
|
|
14
|
-
* @returns {Promise<PaginatedResponse<Award>>} - The awards for the user.
|
|
15
|
-
*/
|
|
16
|
-
export async function fetchAwardsForUser(userId, brand, page = 1, limit = 20) {
|
|
17
|
-
// TODO: connect to the API to get the user awards
|
|
18
|
-
return {
|
|
19
|
-
data: [
|
|
20
|
-
{
|
|
21
|
-
id: 1,
|
|
22
|
-
title: '30-Day Drummer',
|
|
23
|
-
challenge_title: '30-Day Drummer',
|
|
24
|
-
award:
|
|
25
|
-
'https://cdn.sanity.io/files/4032r8py/staging/5c7449ea40f09a096451a48e4a58e4452244e38d.png',
|
|
26
|
-
badge:
|
|
27
|
-
'https://cdn.sanity.io/files/4032r8py/staging/5c7449ea40f09a096451a48e4a58e4452244e38d.png',
|
|
28
|
-
date_completed: '2021-01-01',
|
|
29
|
-
award_text:
|
|
30
|
-
'You received this award for completing 30-Day Drummer with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
31
|
-
description:
|
|
32
|
-
'You received this award for completing 30-Day Drummer with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: 2,
|
|
36
|
-
title: '30-Day Bass',
|
|
37
|
-
challenge_title: '30-Day Bass',
|
|
38
|
-
award:
|
|
39
|
-
'https://cdn.sanity.io/files/4032r8py/production/b7efb6a2f0bd57e87dd13f85b1c3b0c876272b7c.png',
|
|
40
|
-
badge:
|
|
41
|
-
'https://cdn.sanity.io/files/4032r8py/production/b7efb6a2f0bd57e87dd13f85b1c3b0c876272b7c.png',
|
|
42
|
-
date_completed: '2022-02-02',
|
|
43
|
-
award_text:
|
|
44
|
-
'You received this award for completing 30-Day Bass with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
45
|
-
description:
|
|
46
|
-
'You received this award for completing 30-Day Bass with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: 3,
|
|
50
|
-
title: 'New Piano Players Start Here',
|
|
51
|
-
challenge_title: 'New Piano Players Start Here',
|
|
52
|
-
award:
|
|
53
|
-
'https://cdn.sanity.io/files/4032r8py/staging/94e663015da362b41695f5d6f3a8ca3f8317e1b5.png',
|
|
54
|
-
badge:
|
|
55
|
-
'https://cdn.sanity.io/files/4032r8py/staging/94e663015da362b41695f5d6f3a8ca3f8317e1b5.png',
|
|
56
|
-
date_completed: '2023-03-03',
|
|
57
|
-
award_text:
|
|
58
|
-
'You received this award for completing New Piano Players Start Here with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
59
|
-
description:
|
|
60
|
-
'You received this award for completing New Piano Players Start Here with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
id: 4,
|
|
64
|
-
title: 'Ear Training For Singers',
|
|
65
|
-
challenge_title: 'Ear Training For Singers',
|
|
66
|
-
award:
|
|
67
|
-
'https://cdn.sanity.io/files/4032r8py/staging/24fc36e8351d7994bfe8fb5c63c577cf5ed16dc9.png',
|
|
68
|
-
badge:
|
|
69
|
-
'https://cdn.sanity.io/files/4032r8py/staging/24fc36e8351d7994bfe8fb5c63c577cf5ed16dc9.png',
|
|
70
|
-
date_completed: '2024-04-04',
|
|
71
|
-
award_text:
|
|
72
|
-
'You received this award for completing Ear Training For Singers with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
73
|
-
description:
|
|
74
|
-
'You received this award for completing Ear Training For Singers with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
meta: {
|
|
78
|
-
current_page: 1,
|
|
79
|
-
from: 1,
|
|
80
|
-
to: 15,
|
|
81
|
-
per_page: 15,
|
|
82
|
-
last_page: 1,
|
|
83
|
-
total: 4,
|
|
84
|
-
path: 'to be implemented when BE is ready',
|
|
85
|
-
},
|
|
86
|
-
links: {
|
|
87
|
-
first: 'to be implemented when BE is ready',
|
|
88
|
-
last: 'to be implemented when BE is ready',
|
|
89
|
-
next: 'to be implemented when BE is ready',
|
|
90
|
-
prev: 'to be implemented when BE is ready',
|
|
91
|
-
},
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} Award
|
|
3
|
-
* @property {number} id - Unique identifier for the award.
|
|
4
|
-
* @property {string} title - The title of the award.
|
|
5
|
-
* @property {string} badge - URL of the award badge image.
|
|
6
|
-
* @property {string} description - Description of the award.
|
|
7
|
-
* @property {Date} date_completed - Date when the award was completed.
|
|
8
|
-
*/
|