musora-content-services 2.3.21 → 2.3.23

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 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
+ ### [2.3.23](https://github.com/railroadmedia/musora-content-services/compare/v2.3.22...v2.3.23) (2025-05-06)
6
+
7
+ ### [2.3.22](https://github.com/railroadmedia/musora-content-services/compare/v2.3.21...v2.3.22) (2025-05-05)
8
+
5
9
  ### [2.3.21](https://github.com/railroadmedia/musora-content-services/compare/v2.3.20...v2.3.21) (2025-05-02)
6
10
 
7
11
  ### [2.3.20](https://github.com/railroadmedia/musora-content-services/compare/v2.3.19...v2.3.20) (2025-04-30)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.3.21",
3
+ "version": "2.3.23",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.d.ts CHANGED
@@ -29,6 +29,10 @@ import {
29
29
  getTabResults
30
30
  } from './services/content.js';
31
31
 
32
+ import {
33
+ addContextToContent
34
+ } from './services/contentAggregator.js';
35
+
32
36
  import {
33
37
  isContentLiked,
34
38
  isContentLikedByIds,
@@ -215,6 +219,7 @@ import {
215
219
  } from './services/user/sessions.js';
216
220
 
217
221
  import {
222
+ calculateLongestStreaks,
218
223
  createPracticeNotes,
219
224
  deletePracticeSession,
220
225
  getPracticeNotes,
@@ -232,6 +237,7 @@ import {
232
237
 
233
238
  declare module 'musora-content-services' {
234
239
  export {
240
+ addContextToContent,
235
241
  addItemToPlaylist,
236
242
  applyCloudflareWrapper,
237
243
  applySanityTransformations,
@@ -240,6 +246,7 @@ declare module 'musora-content-services' {
240
246
  assignmentStatusReset,
241
247
  blockUser,
242
248
  buildImageSRC,
249
+ calculateLongestStreaks,
243
250
  closeComment,
244
251
  contentStatusCompleted,
245
252
  contentStatusReset,
package/src/index.js CHANGED
@@ -29,6 +29,10 @@ import {
29
29
  getTabResults
30
30
  } from './services/content.js';
31
31
 
32
+ import {
33
+ addContextToContent
34
+ } from './services/contentAggregator.js';
35
+
32
36
  import {
33
37
  isContentLiked,
34
38
  isContentLikedByIds,
@@ -215,6 +219,7 @@ import {
215
219
  } from './services/user/sessions.js';
216
220
 
217
221
  import {
222
+ calculateLongestStreaks,
218
223
  createPracticeNotes,
219
224
  deletePracticeSession,
220
225
  getPracticeNotes,
@@ -231,6 +236,7 @@ import {
231
236
  } from './services/userActivity.js';
232
237
 
233
238
  export {
239
+ addContextToContent,
234
240
  addItemToPlaylist,
235
241
  applyCloudflareWrapper,
236
242
  applySanityTransformations,
@@ -239,6 +245,7 @@ export {
239
245
  assignmentStatusReset,
240
246
  blockUser,
241
247
  buildImageSRC,
248
+ calculateLongestStreaks,
242
249
  closeComment,
243
250
  contentStatusCompleted,
244
251
  contentStatusReset,
@@ -1,8 +1,11 @@
1
- import { getProgressStateByIds, getProgressPercentageByIds, getResumeTimeSecondsByIds } from "./services/contentProgress"
1
+ import { getProgressStateByIds, getProgressPercentageByIds, getResumeTimeSecondsByIds } from "./services/contentProgress"
2
2
  import { isContentLikedByIds } from "./services/contentLikes"
3
3
  import { fetchLikeCount } from "./services/railcontent"
4
4
 
5
- export const addContextToContent = async (dataPromise, ...dataArgs) => {
5
+
6
+
7
+ export async function addContextToContent(dataPromise, ...dataArgs)
8
+ {
6
9
  const lastArg = dataArgs[dataArgs.length - 1]
7
10
  const options = typeof lastArg === 'object' && !Array.isArray(lastArg) ? lastArg : {}
8
11
 
@@ -47,7 +50,7 @@ export const addContextToContent = async (dataPromise, ...dataArgs) => {
47
50
  ...(addLikeCount && ids.length === 1 ? { likeCount: await fetchLikeCount(item.id) } : {}),
48
51
  ...(addResumeTimeSeconds ? { resumeTime: resumeTimeData?.[item.id] } : {}),
49
52
  })
50
-
53
+
51
54
  const newData = Array.isArray(data)
52
55
  ? await Promise.all(data.map(addContext))
53
56
  : await addContext(data)
@@ -290,6 +290,10 @@ async function patchDataHandler(url, data) {
290
290
  return fetchHandler(url, 'patch', null, data)
291
291
  }
292
292
 
293
+ async function putDataHandler(url, data) {
294
+ return fetchHandler(url, 'put', null, data)
295
+ }
296
+
293
297
  async function deleteDataHandler(url, data) {
294
298
  return fetchHandler(url, 'delete')
295
299
  }
@@ -726,7 +730,7 @@ export async function editComment(commentId, comment) {
726
730
  const data = {
727
731
  comment: comment,
728
732
  }
729
- return await patchDataHandler(url, data)
733
+ return await putDataHandler(url, data)
730
734
  }
731
735
 
732
736
  /**
@@ -688,6 +688,87 @@ function calculateStreaks(practices, includeStreakMessage = false) {
688
688
  return { currentDailyStreak, currentWeeklyStreak, streakMessage };
689
689
  }
690
690
 
691
+ /**
692
+ * Calculates the longest daily, weekly streaks and totalPracticeSeconds from user practice dates.
693
+ * @returns {{ longestDailyStreak: number, longestWeeklyStreak: number, totalPracticeSeconds:number }}
694
+ */
695
+ export async function calculateLongestStreaks() {
696
+ let data = await userActivityContext.getData()
697
+ let practices = data?.[DATA_KEY_PRACTICES] ?? {}
698
+ let totalPracticeSeconds = 0;
699
+ // Calculate total practice duration
700
+ for (const date in practices) {
701
+ for (const entry of practices[date]) {
702
+ totalPracticeSeconds += entry.duration_seconds;
703
+ }
704
+ }
705
+
706
+ let practiceDates = Object.keys(practices)
707
+ .map(dateStr => {
708
+ const [y, m, d] = dateStr.split('-').map(Number);
709
+ const newDate = new Date();
710
+ newDate.setFullYear(y, m - 1, d);
711
+ return newDate;
712
+ })
713
+ .sort((a, b) => a - b);
714
+
715
+ if (!practiceDates || practiceDates.length === 0) {
716
+ return {longestDailyStreak: 0, longestWeeklyStreak: 0, totalPracticeSeconds: 0};
717
+ }
718
+
719
+ // Normalize to Date objects
720
+ const normalizedDates = [
721
+ ...new Set(practiceDates.map(d => {
722
+ const date = new Date(d);
723
+ date.setHours(0, 0, 0, 0);
724
+ return date.getTime();
725
+ }))
726
+ ].sort((a, b) => a - b);
727
+
728
+ // ----- Daily Streak -----
729
+ let longestDailyStreak = 1;
730
+ let currentDailyStreak = 1;
731
+ for (let i = 1; i < normalizedDates.length; i++) {
732
+ const diffInDays = (normalizedDates[i] - normalizedDates[i - 1]) / (1000 * 60 * 60 * 24);
733
+ if (diffInDays === 1) {
734
+ currentDailyStreak++;
735
+ longestDailyStreak = Math.max(longestDailyStreak, currentDailyStreak);
736
+ } else {
737
+ currentDailyStreak = 1;
738
+ }
739
+ }
740
+
741
+ // ----- Weekly Streak -----
742
+ const weekStartDates = [
743
+ ...new Set(normalizedDates.map(ts => {
744
+ const d = new Date(ts);
745
+ const day = d.getDay();
746
+ const diff = d.getDate() - day + (day === 0 ? -6 : 1); // adjust to Monday
747
+ d.setDate(diff);
748
+ return d.getTime(); // timestamp for Monday
749
+ }))
750
+ ].sort((a, b) => a - b);
751
+
752
+ let longestWeeklyStreak = 1;
753
+ let currentWeeklyStreak = 1;
754
+
755
+ for (let i = 1; i < weekStartDates.length; i++) {
756
+ const diffInWeeks = (weekStartDates[i] - weekStartDates[i - 1]) / (1000 * 60 * 60 * 24 * 7);
757
+ if (diffInWeeks === 1) {
758
+ currentWeeklyStreak++;
759
+ longestWeeklyStreak = Math.max(longestWeeklyStreak, currentWeeklyStreak);
760
+ } else {
761
+ currentWeeklyStreak = 1;
762
+ }
763
+ }
764
+
765
+ return {
766
+ longestDailyStreak,
767
+ longestWeeklyStreak,
768
+ totalPracticeSeconds
769
+ };
770
+ }
771
+
691
772
 
692
773
 
693
774