musora-content-services 2.140.2 → 2.140.3

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,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.140.3](https://github.com/railroadmedia/musora-content-services/compare/v2.140.2...v2.140.3) (2026-03-17)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **BEHSTP-56:** New And Upcoming Row changes ([#866](https://github.com/railroadmedia/musora-content-services/issues/866)) ([2885f1a](https://github.com/railroadmedia/musora-content-services/commit/2885f1a166810253eadbd6ba9cfa951fc8f5c6cc))
11
+
5
12
  ### [2.140.2](https://github.com/railroadmedia/musora-content-services/compare/v2.140.1...v2.140.2) (2026-03-17)
6
13
 
7
14
  ### [2.140.1](https://github.com/railroadmedia/musora-content-services/compare/v2.140.0...v2.140.1) (2026-03-17)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.140.2",
3
+ "version": "2.140.3",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -355,7 +355,7 @@ const lessonRecentTypes = [
355
355
  'routine',
356
356
  ]
357
357
 
358
- const parentRecentTypes = [
358
+ export const parentRecentTypes = [
359
359
  ...individualLessonsTypes,
360
360
  'skill-pack',
361
361
  ...entertainmentLessonTypes,
@@ -34,7 +34,7 @@ import {
34
34
  liveFields,
35
35
  postProcessBadge,
36
36
  parentField,
37
- grandParentField,
37
+ grandParentField, parentRecentTypes,
38
38
  } from '../contentTypeConfig.js'
39
39
  import { fetchSimilarItems } from './recommendations.js'
40
40
  import { getSongType, processMetadata, ALWAYS_VISIBLE_TABS, CONTENT_STATUSES } from '../contentMetaData.js'
@@ -46,6 +46,8 @@ import { arrayToStringRepresentation, FilterBuilder } from '../filterBuilder.js'
46
46
  import { getPermissionsAdapter } from './permissions/index.ts'
47
47
  import { getAllCompleted, getAllStarted, getAllStartedOrCompleted } from './contentProgress.js'
48
48
  import { fetchRecentActivitiesActiveTabs } from './userActivity.js'
49
+ import { query } from '../lib/sanity/query'
50
+ import { Filters as f } from '../lib/sanity/filter'
49
51
  import { COLLECTION_TYPE } from './sync/models/ContentProgress.js'
50
52
 
51
53
  /**
@@ -388,7 +390,7 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
388
390
  live_event_end_time,
389
391
  "isLive": live_event_start_time <= '${now}' && live_event_end_time >= '${now}'`
390
392
  const query = buildRawQuery(
391
- `defined(live_event_start_time) && (!defined(live_event_end_time) || live_event_end_time >= '${now}' ) && (brand == '${brand}' || brand == 'musora' && live_global_event) && status in ['scheduled']`,
393
+ `defined(live_event_start_time) && (!defined(live_event_end_time) || live_event_end_time >= '${now}' ) && (brand == '${brand}' || live_global_event) && status in ['scheduled']`,
392
394
  fields,
393
395
  {
394
396
  sortOrder: 'published_on asc',
@@ -2049,35 +2051,116 @@ export async function fetchRecent(
2049
2051
 
2050
2052
  export async function fetchScheduledAndNewReleases(
2051
2053
  brand,
2052
- { page = 1, limit = 20, sort = '-published_on' } = {}
2054
+ // page param deprecated, doesnt have 1-1 affect on this functionality
2055
+ // if we want to allow pagination, this requires a revisit
2056
+ { limit = 10 } = {}
2053
2057
  ) {
2054
- const upcomingTypes = getUpcomingEventsTypes(brand)
2055
- const newTypes = getNewReleasesTypes(brand)
2058
+ const maxLessons = 3
2059
+ const maxSongs = 5
2060
+ const maxLivestreams = 2
2056
2061
 
2057
- const scheduledTypes = merge(upcomingTypes, newTypes)
2058
- const typesString = arrayJoinWithQuotes(scheduledTypes)
2059
- const now = getSanityDate(new Date())
2062
+ const rawNow = new Date()
2063
+ const now = getSanityDate(rawNow)
2064
+ const fifteenDaysAgo = getSanityDate(new Date(rawNow - 15 * 24 * 60 * 60 * 1000))
2060
2065
 
2061
- const start = (page - 1) * limit
2062
- const end = start + limit
2063
- const sortOrder = getSortOrder(sort, brand)
2066
+ const parentsWithoutSong = parentRecentTypes.filter(type => type !== 'song')
2064
2067
 
2065
- const query = `
2066
- *[show_in_new_feed == true && (
2067
- (_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && defined(published_on))
2068
- || (
2069
- defined(live_event_start_time)
2070
- && (!defined(live_event_end_time) || live_event_end_time >= '${now}' )
2071
- && brand == '${brand}'
2072
- && status in ['scheduled']
2073
- )
2074
- )] | order(${sortOrder})
2075
- [${start}...${end}]
2076
- {
2077
- ${getFieldsForContentType('new-and-scheduled')}
2068
+ const fields = getFieldsForContentType('new-and-scheduled')
2069
+
2070
+ const lessonFilter = f.combine(
2071
+ "show_in_new_feed == true",
2072
+ f.brand(brand),
2073
+ f.typeIn(parentsWithoutSong),
2074
+ f.statusIn(['published']),
2075
+ f.publishedBefore(now),
2076
+ f.publishedAfter(fifteenDaysAgo),
2077
+ )
2078
+
2079
+ const songFilter = f.combine(
2080
+ "show_in_new_feed == true",
2081
+ f.brand(brand),
2082
+ f.type('song'),
2083
+ f.statusIn(['published']),
2084
+ f.publishedBefore(now),
2085
+ f.publishedAfter(fifteenDaysAgo),
2086
+ )
2087
+
2088
+ const livestreamFilter = f.combine(
2089
+ "show_in_new_feed == true",
2090
+ f.combineOr(
2091
+ f.brand(brand),
2092
+ 'live_global_event == true'
2093
+ ),
2094
+ f.statusIn(['scheduled']),
2095
+ `live_event_start_time >= '${now}'`,
2096
+ )
2097
+
2098
+ const lessonQuery = query()
2099
+ .and(lessonFilter)
2100
+ .order('published_on desc')
2101
+ .slice(0, maxLessons)
2102
+ .select(fields)
2103
+ .build()
2104
+
2105
+ const songQuery = query()
2106
+ .and(songFilter)
2107
+ .order('published_on desc')
2108
+ .slice(0, maxSongs)
2109
+ .select(fields)
2110
+ .build()
2111
+
2112
+ const livestreamQuery = query()
2113
+ .and(livestreamFilter)
2114
+ .order('live_event_start_time asc')
2115
+ .slice(0, maxLivestreams)
2116
+ .select(fields)
2117
+ .build()
2118
+
2119
+ const q = `{
2120
+ "lessons": ${lessonQuery},
2121
+ "songs": ${songQuery},
2122
+ "livestreams": ${livestreamQuery}
2078
2123
  }`
2079
2124
 
2080
- return fetchSanity(query, true)
2125
+ const r = await fetchSanity(q, true)
2126
+
2127
+ if (!r) {
2128
+ return []
2129
+ }
2130
+
2131
+ return reorderScheduledAndNewReleases(r, limit)
2132
+ }
2133
+
2134
+ function reorderScheduledAndNewReleases(r, limit) {
2135
+ let lessonLimit, songLimit, livestreamLimit
2136
+ // discrete limit/order behaviour for this row
2137
+ if (limit >= 10) {
2138
+ lessonLimit = 3
2139
+ songLimit = 5
2140
+ livestreamLimit = 2
2141
+ } else if (limit >= 3) {
2142
+ lessonLimit = 2
2143
+ livestreamLimit = 1
2144
+ songLimit = limit - lessonLimit - livestreamLimit
2145
+ } else {
2146
+ lessonLimit = (limit > 0) ? 1 : 0
2147
+ livestreamLimit = 0
2148
+ songLimit = limit - lessonLimit
2149
+ }
2150
+
2151
+ const lessons = r.lessons.slice(0, lessonLimit)
2152
+ if (lessons.length < lessonLimit) {
2153
+ songLimit += (lessonLimit - lessons.length)
2154
+ }
2155
+
2156
+ const livestreams = r.livestreams.slice(0, livestreamLimit)
2157
+ if (livestreams.length < livestreamLimit) {
2158
+ songLimit += (livestreamLimit - livestreams.length)
2159
+ }
2160
+
2161
+ const songs = r.songs.slice(0, songLimit)
2162
+
2163
+ return [...lessons, ...songs, ...livestreams]
2081
2164
  }
2082
2165
 
2083
2166
  export async function fetchShows(brand, type, sort = 'sort') {