musora-content-services 2.131.4 → 2.131.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/.claude/settings.local.json +9 -0
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/services/progress-row/base.js +92 -59
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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.131.5](https://github.com/railroadmedia/musora-content-services/compare/v2.131.4...v2.131.5) (2026-02-05)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **T3PS-1401:** 5 minute ttl for pinned data ([#767](https://github.com/railroadmedia/musora-content-services/issues/767)) ([a14cea5](https://github.com/railroadmedia/musora-content-services/commit/a14cea5ce9e65297e5f16c9ab320445786da0c5d))
|
|
11
|
+
|
|
5
12
|
### [2.131.4](https://github.com/railroadmedia/musora-content-services/compare/v2.131.3...v2.131.4) (2026-02-05)
|
|
6
13
|
|
|
7
14
|
|
package/package.json
CHANGED
|
@@ -14,54 +14,29 @@ import { fetchByRailContentIds } from '../sanity.js'
|
|
|
14
14
|
import { addContextToContent } from '../contentAggregator.js'
|
|
15
15
|
import { fetchPlaylist } from '../content-org/playlists.js'
|
|
16
16
|
import { TabResponseType } from '../../contentMetaData.js'
|
|
17
|
-
import { PUT } from '../../infrastructure/http/HttpClient.ts'
|
|
17
|
+
import { GET, PUT } from '../../infrastructure/http/HttpClient.ts'
|
|
18
18
|
import { postProcessBadge } from "../../contentTypeConfig.js";
|
|
19
19
|
|
|
20
20
|
export const USER_PIN_PROGRESS_KEY = 'user_pin_progress_row'
|
|
21
|
+
const CACHE_EXPIRY_MS = 5 * 60 * 1000
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*/
|
|
25
|
-
export function getUserPinProgressKey(id) {
|
|
26
|
-
const userId = id || globalConfig.sessionConfig?.userId || globalConfig.railcontentConfig?.userId
|
|
27
|
-
return userId ? `user_pin_progress_row_${userId}` : USER_PIN_PROGRESS_KEY
|
|
28
|
-
}
|
|
23
|
+
async function getUserPinnedItem(brand) {
|
|
24
|
+
const key = getUserPinProgressKey()
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* @param {number} [options.limit=8] - Maximum number of progress rows to return.
|
|
36
|
-
* @returns {Promise<Object>} - A promise that resolves to an object containing progress rows formatted for UI.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* getProgressRows({ brand: 'drumeo', limit: 10 })
|
|
40
|
-
* .then(data => console.log(data))
|
|
41
|
-
* .catch(error => console.error(error));
|
|
42
|
-
*/
|
|
43
|
-
export async function getProgressRows({ brand = 'drumeo', limit = 8 } = {}) {
|
|
44
|
-
const [userPinnedItem, recentPlaylists] = await Promise.all([
|
|
45
|
-
getUserPinnedItem(brand),
|
|
46
|
-
getRecentPlaylists(brand, limit),
|
|
47
|
-
])
|
|
48
|
-
const playlistEngagedOnContent = await getPlaylistEngagedOnContent(recentPlaylists)
|
|
49
|
-
const [contentCardMap, playlistCards, methodCard] = await Promise.all([
|
|
50
|
-
getContentCardMap(brand, limit, playlistEngagedOnContent, userPinnedItem),
|
|
51
|
-
getPlaylistCards(recentPlaylists),
|
|
52
|
-
getMethodCard(brand),
|
|
53
|
-
])
|
|
54
|
-
const pinnedCard = await popPinnedItem(userPinnedItem, contentCardMap, playlistCards, methodCard)
|
|
55
|
-
let allResultsLength = playlistCards.length + contentCardMap.size
|
|
56
|
-
if (methodCard) {
|
|
57
|
-
allResultsLength += 1
|
|
26
|
+
const pinnedProgress = await getStoredPinnedData(key)
|
|
27
|
+
const cachedData = pinnedProgress[brand]
|
|
28
|
+
|
|
29
|
+
if (isCacheValid(cachedData)) {
|
|
30
|
+
return cachedData
|
|
58
31
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
32
|
+
|
|
33
|
+
const url = `/api/user-management-system/v1/progress/pin?brand=${brand}`
|
|
34
|
+
const response = await GET(url)
|
|
35
|
+
|
|
36
|
+
if (response && !response.error) {
|
|
37
|
+
return await setUserBrandPinnedItem(brand, response)
|
|
64
38
|
}
|
|
39
|
+
return response
|
|
65
40
|
}
|
|
66
41
|
|
|
67
42
|
/**
|
|
@@ -80,11 +55,11 @@ export async function getProgressRows({ brand = 'drumeo', limit = 8 } = {}) {
|
|
|
80
55
|
export async function pinProgressRow(brand, id, progressType) {
|
|
81
56
|
const url = `/api/user-management-system/v1/progress/pin?brand=${brand}&id=${id}&progressType=${progressType}`
|
|
82
57
|
const response = await PUT(url, null)
|
|
58
|
+
|
|
83
59
|
if (response && !response.error) {
|
|
84
|
-
await
|
|
60
|
+
return await setUserBrandPinnedItem(brand, {
|
|
85
61
|
id,
|
|
86
62
|
progressType,
|
|
87
|
-
pinnedAt: new Date().toISOString(),
|
|
88
63
|
})
|
|
89
64
|
}
|
|
90
65
|
return response
|
|
@@ -105,22 +80,90 @@ export async function unpinProgressRow(brand) {
|
|
|
105
80
|
const url = `/api/user-management-system/v1/progress/unpin?brand=${brand}`
|
|
106
81
|
const response = await PUT(url, null)
|
|
107
82
|
if (response && !response.error) {
|
|
108
|
-
await
|
|
83
|
+
await setUserBrandPinnedItem(brand, null)
|
|
109
84
|
}
|
|
110
85
|
return response
|
|
111
86
|
}
|
|
112
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Gets the localStorage key for user pinned progress, scoped by user ID
|
|
90
|
+
*/
|
|
91
|
+
export function getUserPinProgressKey(id) {
|
|
92
|
+
const userId = id || globalConfig.sessionConfig?.userId || globalConfig.railcontentConfig?.userId
|
|
93
|
+
return USER_PIN_PROGRESS_KEY + `_${userId}`
|
|
94
|
+
}
|
|
95
|
+
|
|
113
96
|
export async function setUserPinnedProgressRow(userId, pinnedData) {
|
|
114
97
|
const key = getUserPinProgressKey(userId)
|
|
98
|
+
|
|
99
|
+
pinnedData.map(item => ({...item, cachedAt: Date.now()}))
|
|
115
100
|
await globalConfig.localStorage.setItem(key, JSON.stringify(pinnedData))
|
|
116
101
|
}
|
|
117
102
|
|
|
118
|
-
async function
|
|
103
|
+
async function setUserBrandPinnedItem(brand, pinnedData) {
|
|
104
|
+
if (!brand) return
|
|
105
|
+
|
|
119
106
|
const key = getUserPinProgressKey()
|
|
107
|
+
let pinnedProgress = await getStoredPinnedData(key)
|
|
108
|
+
|
|
109
|
+
pinnedProgress[brand] = setPinnedData(pinnedData)
|
|
110
|
+
await globalConfig.localStorage.setItem(key, JSON.stringify(pinnedProgress))
|
|
111
|
+
return pinnedProgress
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function getStoredPinnedData(key) {
|
|
120
115
|
const pinnedProgressRaw = await globalConfig.localStorage.getItem(key)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
const pinnedProgress = pinnedProgressRaw ? JSON.parse(pinnedProgressRaw) : {}
|
|
117
|
+
return pinnedProgress || {}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function setPinnedData(pinnedData) {
|
|
121
|
+
const now = Date.now()
|
|
122
|
+
return {
|
|
123
|
+
...pinnedData,
|
|
124
|
+
cachedAt: now
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isCacheValid(cachedData) {
|
|
129
|
+
return cachedData?.cachedAt && (Date.now() - cachedData.cachedAt) < CACHE_EXPIRY_MS
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Fetches and combines recent user progress rows and playlists, excluding certain types and parents.
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} [options={}] - Options for fetching progress rows.
|
|
136
|
+
* @param {string|null} [options.brand=null] - The brand context for progress data.
|
|
137
|
+
* @param {number} [options.limit=8] - Maximum number of progress rows to return.
|
|
138
|
+
* @returns {Promise<Object>} - A promise that resolves to an object containing progress rows formatted for UI.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* getProgressRows({ brand: 'drumeo', limit: 10 })
|
|
142
|
+
* .then(data => console.log(data))
|
|
143
|
+
* .catch(error => console.error(error));
|
|
144
|
+
*/
|
|
145
|
+
export async function getProgressRows({ brand = 'drumeo', limit = 8 } = {}) {
|
|
146
|
+
const [userPinnedItem, recentPlaylists] = await Promise.all([
|
|
147
|
+
getUserPinnedItem(brand),
|
|
148
|
+
getRecentPlaylists(brand, limit),
|
|
149
|
+
])
|
|
150
|
+
const playlistEngagedOnContent = await getPlaylistEngagedOnContent(recentPlaylists)
|
|
151
|
+
const [contentCardMap, playlistCards, methodCard] = await Promise.all([
|
|
152
|
+
getContentCardMap(brand, limit, playlistEngagedOnContent, userPinnedItem),
|
|
153
|
+
getPlaylistCards(recentPlaylists),
|
|
154
|
+
getMethodCard(brand),
|
|
155
|
+
])
|
|
156
|
+
const pinnedCard = await popPinnedItem(userPinnedItem, contentCardMap, playlistCards, methodCard)
|
|
157
|
+
let allResultsLength = playlistCards.length + contentCardMap.size
|
|
158
|
+
if (methodCard) {
|
|
159
|
+
allResultsLength += 1
|
|
160
|
+
}
|
|
161
|
+
const results = sortCards(pinnedCard, contentCardMap, playlistCards, methodCard, limit)
|
|
162
|
+
return {
|
|
163
|
+
type: TabResponseType.PROGRESS_ROWS,
|
|
164
|
+
displayBrowseAll: allResultsLength > limit,
|
|
165
|
+
data: results,
|
|
166
|
+
}
|
|
124
167
|
}
|
|
125
168
|
|
|
126
169
|
/**
|
|
@@ -131,7 +174,6 @@ async function getUserPinnedItem(brand) {
|
|
|
131
174
|
async function popPinnedItem(userPinnedItem, contentCardMap, playlistCards, methodCard) {
|
|
132
175
|
if (!userPinnedItem) return null
|
|
133
176
|
const pinnedId = parseInt(userPinnedItem.id)
|
|
134
|
-
const pinnedAt = userPinnedItem.pinnedAt
|
|
135
177
|
const progressType = userPinnedItem.progressType ?? userPinnedItem.type
|
|
136
178
|
|
|
137
179
|
let item = null
|
|
@@ -163,7 +205,7 @@ async function popPinnedItem(userPinnedItem, contentCardMap, playlistCards, meth
|
|
|
163
205
|
id: pinnedId,
|
|
164
206
|
playlist: playlist,
|
|
165
207
|
type: 'playlist',
|
|
166
|
-
progressTimestamp: new Date(
|
|
208
|
+
progressTimestamp: new Date().getTime(),
|
|
167
209
|
})
|
|
168
210
|
}
|
|
169
211
|
} else if (progressType === 'method') {
|
|
@@ -218,12 +260,3 @@ function mergeAndSortItems(items, limit) {
|
|
|
218
260
|
})
|
|
219
261
|
.slice(0, limit)
|
|
220
262
|
}
|
|
221
|
-
|
|
222
|
-
async function updateUserPinnedProgressRow(brand, pinnedData) {
|
|
223
|
-
const key = getUserPinProgressKey()
|
|
224
|
-
const pinnedProgressRaw = await globalConfig.localStorage.getItem(key)
|
|
225
|
-
let pinnedProgress = pinnedProgressRaw ? JSON.parse(pinnedProgressRaw) : {}
|
|
226
|
-
pinnedProgress = pinnedProgress || {}
|
|
227
|
-
pinnedProgress[brand] = pinnedData
|
|
228
|
-
await globalConfig.localStorage.setItem(key, JSON.stringify(pinnedProgress))
|
|
229
|
-
}
|