musora-content-services 2.107.2 → 2.107.4

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.
Files changed (76) hide show
  1. package/.claude/settings.local.json +6 -9
  2. package/.yarnrc.yml +1 -0
  3. package/CHANGELOG.md +18 -0
  4. package/check_content.js +30 -0
  5. package/check_content.mjs +32 -0
  6. package/package.json +1 -1
  7. package/src/filterBuilder.js +0 -0
  8. package/src/index.d.ts +17 -0
  9. package/src/index.js +17 -0
  10. package/src/infrastructure/http/HttpClient.ts +0 -0
  11. package/src/lib/ads/monoid.ts +0 -0
  12. package/src/lib/ads/semigroup.ts +0 -0
  13. package/src/lib/sanity/filter.ts +0 -0
  14. package/src/lib/sanity/query.ts +0 -0
  15. package/src/services/awards/award-callbacks.js +0 -0
  16. package/src/services/awards/award-query.js +0 -0
  17. package/src/services/awards/internal/award-definitions.js +0 -0
  18. package/src/services/awards/internal/award-manager.js +0 -0
  19. package/src/services/awards/internal/certificate-builder.js +0 -0
  20. package/src/services/awards/types.d.ts +0 -0
  21. package/src/services/awards/types.js +0 -0
  22. package/src/services/content/artist.ts +5 -5
  23. package/src/services/content/genre.ts +5 -5
  24. package/src/services/content/instructor.ts +7 -6
  25. package/src/services/content-org/guided-courses.ts +0 -0
  26. package/src/services/content-org/learning-paths.ts +0 -0
  27. package/src/services/content-org/playlists.js +0 -0
  28. package/src/services/content.js +0 -0
  29. package/src/services/contentAggregator.js +0 -0
  30. package/src/services/contentProgress.js +0 -0
  31. package/src/services/forums/forums.ts +0 -0
  32. package/src/services/forums/posts.ts +50 -2
  33. package/src/services/forums/threads.ts +13 -0
  34. package/src/services/permissions/PermissionsV2Adapter.ts +0 -0
  35. package/src/services/progress-events.js +0 -0
  36. package/src/services/progress-row/method-card.js +0 -0
  37. package/src/services/railcontent.js +80 -3
  38. package/src/services/recommendations.js +0 -0
  39. package/src/services/reporting/reporting.ts +71 -3
  40. package/src/services/sync/errors/index.ts +0 -0
  41. package/src/services/sync/errors/validators.ts +0 -0
  42. package/src/services/sync/fetch.ts +0 -0
  43. package/src/services/sync/manager.ts +0 -0
  44. package/src/services/sync/models/ContentLike.ts +0 -0
  45. package/src/services/sync/models/ContentProgress.ts +0 -0
  46. package/src/services/sync/models/Practice.ts +0 -0
  47. package/src/services/sync/models/PracticeDayNote.ts +0 -0
  48. package/src/services/sync/models/UserAwardProgress.ts +0 -0
  49. package/src/services/sync/repositories/content-progress.ts +0 -0
  50. package/src/services/sync/repositories/user-award-progress.ts +0 -0
  51. package/src/services/sync/retry.ts +0 -0
  52. package/src/services/sync/schema/index.ts +0 -0
  53. package/src/services/sync/store/index.ts +0 -0
  54. package/src/services/urlBuilder.ts +297 -0
  55. package/src/services/user/chat.js +0 -0
  56. package/src/services/user/interests.js +0 -0
  57. package/src/services/user/management.js +0 -0
  58. package/src/services/user/memberships.ts +0 -0
  59. package/src/services/user/notifications.js +0 -0
  60. package/src/services/user/onboarding.ts +12 -122
  61. package/src/services/user/profile.js +0 -0
  62. package/src/services/user/sessions.js +0 -0
  63. package/src/services/user/types.d.ts +0 -0
  64. package/src/services/user/types.js +0 -0
  65. package/src/services/userActivity.js +0 -0
  66. package/test/awards/award-completion-flow.test.js +0 -0
  67. package/test/initializeTests.js +0 -0
  68. package/test/learningPaths.test.js +0 -0
  69. package/test/lib/__snapshots__/filter.test.ts.snap +0 -0
  70. package/test/lib/filter.test.ts +0 -0
  71. package/test/lib/query.test.ts +0 -0
  72. package/test/reporting.test.js +132 -0
  73. package/test/sanityQueryService.test.js +0 -0
  74. package/test/sync/adapter.ts +0 -0
  75. package/test/sync/initialize-sync-manager.js +0 -0
  76. package/test_owned_navigate.js +74 -0
@@ -1,17 +1,14 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
+ "Read(//app/musora-platform-backend/**)",
5
+ "Read(//app/musora-platform-frontend/**)",
4
6
  "Bash(find:*)",
5
- "Bash(docker exec:*)",
6
- "Bash(npm test:*)",
7
- "WebSearch",
8
- "WebFetch(domain:watermelondb.dev)",
9
- "WebFetch(domain:github.com)",
10
- "Bash(git checkout:*)",
11
- "Bash(npm run doc:*)",
7
+ "Bash(sed:*)",
8
+ "Read(//app/**)",
12
9
  "Bash(cat:*)",
13
- "Bash(tr:*)",
14
- "Bash(npm run build-index:*)"
10
+ "Bash(docker exec:*)",
11
+ "Bash(npm config:*)"
15
12
  ],
16
13
  "deny": [],
17
14
  "ask": []
package/.yarnrc.yml ADDED
@@ -0,0 +1 @@
1
+ nodeLinker: node-modules
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
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.107.4](https://github.com/railroadmedia/musora-content-services/compare/v2.107.1...v2.107.4) (2025-12-22)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **AGI:** contentType optional ([deae293](https://github.com/railroadmedia/musora-content-services/commit/deae2934c26ea0b9ee4d68c736fbb5753616a5e9))
11
+ * **AGI:** return children on AGI lessons functions ([#671](https://github.com/railroadmedia/musora-content-services/issues/671)) ([92ed791](https://github.com/railroadmedia/musora-content-services/commit/92ed791ce563aa4f1bf16cb41d2b6fe421504064))
12
+ * **onboarding:** call BE for recommendation ([#670](https://github.com/railroadmedia/musora-content-services/issues/670)) ([a876736](https://github.com/railroadmedia/musora-content-services/commit/a8767363ff3428638bde477cd6c30e5bf61179b2))
13
+
14
+ ### [2.107.3](https://github.com/railroadmedia/musora-content-services/compare/v2.107.2...v2.107.3) (2025-12-19)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **AGI:** contentType optional ([deae293](https://github.com/railroadmedia/musora-content-services/commit/deae2934c26ea0b9ee4d68c736fbb5753616a5e9))
20
+ * **AGI:** return children on AGI lessons functions ([#671](https://github.com/railroadmedia/musora-content-services/issues/671)) ([92ed791](https://github.com/railroadmedia/musora-content-services/commit/92ed791ce563aa4f1bf16cb41d2b6fe421504064))
21
+ * **onboarding:** call BE for recommendation ([#670](https://github.com/railroadmedia/musora-content-services/issues/670)) ([a876736](https://github.com/railroadmedia/musora-content-services/commit/a8767363ff3428638bde477cd6c30e5bf61179b2))
22
+
5
23
  ### [2.107.2](https://github.com/railroadmedia/musora-content-services/compare/v2.107.1...v2.107.2) (2025-12-19)
6
24
 
7
25
  ### [2.107.1](https://github.com/railroadmedia/musora-content-services/compare/v2.107.0...v2.107.1) (2025-12-19)
@@ -0,0 +1,30 @@
1
+ const { initializeService } = require('./src/services/config.js');
2
+ const { fetchByRailContentIds } = require('./src/services/sanity.js');
3
+ require('dotenv/config');
4
+
5
+ async function checkContent() {
6
+ initializeService({
7
+ sanityConfig: {
8
+ token: process.env.SANITY_TOKEN,
9
+ projectId: process.env.SANITY_PROJECT_ID,
10
+ dataset: process.env.SANITY_DATASET,
11
+ version: process.env.SANITY_VERSION || '2021-06-07',
12
+ },
13
+ railcontentConfig: {
14
+ token: process.env.RAILCONTENT_TOKEN,
15
+ userId: process.env.RAILCONTENT_USER_ID,
16
+ baseUrl: process.env.RAILCONTENT_BASE_URL,
17
+ authToken: process.env.RAILCONTENT_AUTH_TOKEN,
18
+ },
19
+ baseUrl: process.env.RAILCONTENT_BASE_URL,
20
+ localStorage: null,
21
+ isMA: false,
22
+ });
23
+
24
+ console.log('Checking railcontent_id: 421814');
25
+ const contents = await fetchByRailContentIds([421814]);
26
+ console.log('Results:', JSON.stringify(contents, null, 2));
27
+ console.log('Found:', contents.length, 'items');
28
+ }
29
+
30
+ checkContent().catch(console.error);
@@ -0,0 +1,32 @@
1
+ import { initializeService } from './src/services/config.js';
2
+ import { fetchByRailContentIds } from './src/services/sanity.js';
3
+ import dotenv from 'dotenv';
4
+
5
+ dotenv.config();
6
+
7
+ async function checkContent() {
8
+ initializeService({
9
+ sanityConfig: {
10
+ token: process.env.SANITY_TOKEN,
11
+ projectId: process.env.SANITY_PROJECT_ID,
12
+ dataset: process.env.SANITY_DATASET,
13
+ version: process.env.SANITY_VERSION || '2021-06-07',
14
+ },
15
+ railcontentConfig: {
16
+ token: process.env.RAILCONTENT_TOKEN,
17
+ userId: process.env.RAILCONTENT_USER_ID,
18
+ baseUrl: process.env.RAILCONTENT_BASE_URL,
19
+ authToken: process.env.RAILCONTENT_AUTH_TOKEN,
20
+ },
21
+ baseUrl: process.env.RAILCONTENT_BASE_URL,
22
+ localStorage: null,
23
+ isMA: false,
24
+ });
25
+
26
+ console.log('Checking railcontent_id: 421814');
27
+ const contents = await fetchByRailContentIds([421814]);
28
+ console.log('Results:', JSON.stringify(contents, null, 2));
29
+ console.log('Found:', contents.length, 'items');
30
+ }
31
+
32
+ checkContent().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.107.2",
3
+ "version": "2.107.4",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
package/src/index.d.ts CHANGED
@@ -155,6 +155,7 @@ import {
155
155
  createPost,
156
156
  deletePost,
157
157
  fetchCommunityGuidelines,
158
+ fetchPost,
158
159
  fetchPosts,
159
160
  jumpToPost,
160
161
  likePost,
@@ -168,6 +169,7 @@ import {
168
169
  deleteThread,
169
170
  fetchFollowedThreads,
170
171
  fetchLatestThreads,
172
+ fetchThread,
171
173
  fetchThreads,
172
174
  followThread,
173
175
  lockThread,
@@ -310,6 +312,14 @@ import {
310
312
  jumpToContinueContent
311
313
  } from './services/sanity.js';
312
314
 
315
+ import {
316
+ generateCommentUrl,
317
+ generateContentUrl,
318
+ generateContentUrlWithDomain,
319
+ generateForumPostUrl,
320
+ generatePlaylistUrl
321
+ } from './services/urlBuilder.ts';
322
+
313
323
  import {
314
324
  confirmEmailChange,
315
325
  deleteAccount,
@@ -543,6 +553,7 @@ declare module 'musora-content-services' {
543
553
  fetchPlayAlongsCount,
544
554
  fetchPlaylist,
545
555
  fetchPlaylistItems,
556
+ fetchPost,
546
557
  fetchPosts,
547
558
  fetchRecent,
548
559
  fetchRecentActivitiesActiveTabs,
@@ -563,6 +574,7 @@ declare module 'musora-content-services' {
563
574
  fetchSongById,
564
575
  fetchSongsInProgress,
565
576
  fetchTabData,
577
+ fetchThread,
566
578
  fetchThreads,
567
579
  fetchTopComment,
568
580
  fetchTopLevelParentId,
@@ -580,6 +592,11 @@ declare module 'musora-content-services' {
580
592
  findIncompleteLesson,
581
593
  followThread,
582
594
  generateAuthSessionUrl,
595
+ generateCommentUrl,
596
+ generateContentUrl,
597
+ generateContentUrlWithDomain,
598
+ generateForumPostUrl,
599
+ generatePlaylistUrl,
583
600
  getActiveDiscussions,
584
601
  getActivePath,
585
602
  getAllCompleted,
package/src/index.js CHANGED
@@ -159,6 +159,7 @@ import {
159
159
  createPost,
160
160
  deletePost,
161
161
  fetchCommunityGuidelines,
162
+ fetchPost,
162
163
  fetchPosts,
163
164
  jumpToPost,
164
165
  likePost,
@@ -172,6 +173,7 @@ import {
172
173
  deleteThread,
173
174
  fetchFollowedThreads,
174
175
  fetchLatestThreads,
176
+ fetchThread,
175
177
  fetchThreads,
176
178
  followThread,
177
179
  lockThread,
@@ -314,6 +316,14 @@ import {
314
316
  jumpToContinueContent
315
317
  } from './services/sanity.js';
316
318
 
319
+ import {
320
+ generateCommentUrl,
321
+ generateContentUrl,
322
+ generateContentUrlWithDomain,
323
+ generateForumPostUrl,
324
+ generatePlaylistUrl
325
+ } from './services/urlBuilder.ts';
326
+
317
327
  import {
318
328
  confirmEmailChange,
319
329
  deleteAccount,
@@ -542,6 +552,7 @@ export {
542
552
  fetchPlayAlongsCount,
543
553
  fetchPlaylist,
544
554
  fetchPlaylistItems,
555
+ fetchPost,
545
556
  fetchPosts,
546
557
  fetchRecent,
547
558
  fetchRecentActivitiesActiveTabs,
@@ -562,6 +573,7 @@ export {
562
573
  fetchSongById,
563
574
  fetchSongsInProgress,
564
575
  fetchTabData,
576
+ fetchThread,
565
577
  fetchThreads,
566
578
  fetchTopComment,
567
579
  fetchTopLevelParentId,
@@ -579,6 +591,11 @@ export {
579
591
  findIncompleteLesson,
580
592
  followThread,
581
593
  generateAuthSessionUrl,
594
+ generateCommentUrl,
595
+ generateContentUrl,
596
+ generateContentUrlWithDomain,
597
+ generateForumPostUrl,
598
+ generatePlaylistUrl,
582
599
  getActiveDiscussions,
583
600
  getActivePath,
584
601
  getAllCompleted,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @module Artist
3
3
  */
4
- import { getFieldsForContentType } from '../../contentTypeConfig.js'
4
+ import { getFieldsForContentTypeWithFilteredChildren } from '../../contentTypeConfig.js'
5
+ import { Brands } from '../../lib/brands'
6
+ import { Filters as f } from '../../lib/sanity/filter'
5
7
  import { BuildQueryOptions, query } from '../../lib/sanity/query'
6
8
  import { fetchSanity, getSortOrder } from '../sanity.js'
7
9
  import { Lesson } from './content'
8
- import { Brands } from '../../lib/brands'
9
- import { Filters as f } from '../../lib/sanity/filter'
10
10
 
11
11
  export interface Artist {
12
12
  slug: string
@@ -123,7 +123,7 @@ export interface ArtistLessons {
123
123
  export async function fetchArtistLessons(
124
124
  slug: string,
125
125
  brand: Brands | string,
126
- contentType: string,
126
+ contentType?: string,
127
127
  {
128
128
  sort = '-published_on',
129
129
  searchTerm = '',
@@ -150,7 +150,7 @@ export async function fetchArtistLessons(
150
150
  .and(restrictions)
151
151
  .order(sort)
152
152
  .slice(offset, limit)
153
- .select(getFieldsForContentType(contentType) as string)
153
+ .select((await getFieldsForContentTypeWithFilteredChildren(contentType, true)) as string)
154
154
  .build()
155
155
 
156
156
  const total = query().and(restrictions).build()
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @module Genre
3
3
  */
4
- import { getFieldsForContentType } from '../../contentTypeConfig.js'
5
- import { fetchSanity, getSortOrder } from '../sanity.js'
6
- import { Lesson } from './content'
7
- import { BuildQueryOptions, query } from '../../lib/sanity/query'
4
+ import { getFieldsForContentTypeWithFilteredChildren } from '../../contentTypeConfig.js'
8
5
  import { Brands } from '../../lib/brands'
9
6
  import { Filters as f } from '../../lib/sanity/filter'
7
+ import { BuildQueryOptions, query } from '../../lib/sanity/query'
8
+ import { fetchSanity, getSortOrder } from '../sanity.js'
9
+ import { Lesson } from './content'
10
10
 
11
11
  export interface Genre {
12
12
  name: string
@@ -149,7 +149,7 @@ export async function fetchGenreLessons(
149
149
  .and(restrictions)
150
150
  .order(sort)
151
151
  .slice(offset, limit)
152
- .select(getFieldsForContentType(contentType) as string)
152
+ .select((await getFieldsForContentTypeWithFilteredChildren(contentType, true)) as string)
153
153
  .build()
154
154
 
155
155
  const total = query().and(restrictions).build()
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @module Instructor
3
3
  */
4
- import { getFieldsForContentType } from '../../contentTypeConfig.js'
5
- import { fetchSanity, getSortOrder } from '../sanity.js'
6
- import { Lesson } from './content'
7
- import { BuildQueryOptions, query } from '../../lib/sanity/query'
4
+ import { getFieldsForContentTypeWithFilteredChildren } from '../../contentTypeConfig.js'
8
5
  import { Brands } from '../../lib/brands'
9
6
  import { Filters as f } from '../../lib/sanity/filter'
7
+ import { BuildQueryOptions, query } from '../../lib/sanity/query'
8
+ import { fetchSanity, getSortOrder } from '../sanity.js'
9
+ import { Lesson } from './content'
10
10
 
11
11
  export interface Instructor {
12
12
  lesson_count: number
@@ -106,7 +106,7 @@ export interface InstructorLessons {
106
106
  * Fetch the data needed for the instructor screen.
107
107
  * @param {string} slug - The slug of the instructor
108
108
  * @param {Brands|string} brand - The brand for which to fetch instructor lessons
109
- *
109
+ * @param {string|null} [contentType] - The content type to filter lessons by (e.g., 'lesson', 'course').
110
110
  * @param {FetchInstructorLessonsOptions} options - Parameters for pagination, filtering and sorting.
111
111
  * @param {string} [options.sortOrder="-published_on"] - The field to sort the lessons by.
112
112
  * @param {string} [options.searchTerm=""] - The search term to filter content by title.
@@ -123,6 +123,7 @@ export interface InstructorLessons {
123
123
  export async function fetchInstructorLessons(
124
124
  slug: string,
125
125
  brand: Brands | string,
126
+ contentType?: string,
126
127
  {
127
128
  sort = '-published_on',
128
129
  searchTerm = '',
@@ -147,7 +148,7 @@ export async function fetchInstructorLessons(
147
148
  .and(restrictions)
148
149
  .order(sort)
149
150
  .slice(offset, limit)
150
- .select(getFieldsForContentType() as string)
151
+ .select((await getFieldsForContentTypeWithFilteredChildren(contentType, true)) as string)
151
152
  .build()
152
153
 
153
154
  const total = query().and(restrictions).build()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -23,8 +23,26 @@ export interface CreatePostParams {
23
23
  * @throws {HttpError} - If the request fails.
24
24
  */
25
25
  export async function createPost(threadId: number, params: CreatePostParams): Promise<ForumPost> {
26
+ const { generateForumPostUrl } = await import('../urlBuilder.ts')
27
+ const { fetchThread } = await import('./threads.ts')
28
+
29
+ // Fetch thread to get category_id for URL generation
30
+ const thread = await fetchThread(threadId, params.brand)
31
+
32
+ // Generate forum post URL
33
+ const contentUrl = generateForumPostUrl({
34
+ brand: params.brand,
35
+ thread: {
36
+ category_id: thread.category_id,
37
+ id: threadId
38
+ }
39
+ }, false)
40
+
26
41
  const httpClient = new HttpClient(globalConfig.baseUrl)
27
- return httpClient.post<ForumPost>(`${baseUrl}/v1/threads/${threadId}/posts`, params)
42
+ return httpClient.post<ForumPost>(`${baseUrl}/v1/threads/${threadId}/posts`, {
43
+ ...params,
44
+ content_url: contentUrl
45
+ })
28
46
  }
29
47
 
30
48
  /**
@@ -40,6 +58,19 @@ export async function updatePost(postId: number, params: CreatePostParams): Prom
40
58
  return httpClient.put<ForumPost>(`${baseUrl}/v1/posts/${postId}`, params)
41
59
  }
42
60
 
61
+ /**
62
+ * Fetches a single forum post by ID.
63
+ *
64
+ * @param {number} postId - The ID of the post to fetch.
65
+ * @param {string} brand - The brand context (e.g., "drumeo", "singeo").
66
+ * @returns {Promise<ForumPost>} - A promise that resolves to the forum post.
67
+ * @throws {HttpError} - If the HTTP request fails.
68
+ */
69
+ export async function fetchPost(postId: number, brand: string): Promise<ForumPost> {
70
+ const httpClient = new HttpClient(globalConfig.baseUrl)
71
+ return httpClient.get<ForumPost>(`${baseUrl}/v1/posts/${postId}?brand=${brand}`)
72
+ }
73
+
43
74
  export interface FetchPostParams {
44
75
  page?: number
45
76
  limit?: number
@@ -91,8 +122,25 @@ export async function fetchPosts(
91
122
  * @throws {HttpError} - If the request fails.
92
123
  */
93
124
  export async function likePost(postId: number, brand: string): Promise<void> {
125
+ const { generateForumPostUrl } = await import('../urlBuilder.ts')
126
+
127
+ // Fetch post to get thread info for URL generation
128
+ const post = await fetchPost(postId, brand)
129
+
130
+ // Generate forum post URL
131
+ const contentUrl = generateForumPostUrl({
132
+ brand,
133
+ thread: {
134
+ category_id: post.thread.category_id,
135
+ id: post.thread.id
136
+ }
137
+ }, false)
138
+
94
139
  const httpClient = new HttpClient(globalConfig.baseUrl)
95
- return httpClient.post<void>(`${baseUrl}/v1/posts/${postId}/likes`, { brand })
140
+ return httpClient.post<void>(`${baseUrl}/v1/posts/${postId}/likes`, {
141
+ brand,
142
+ content_url: contentUrl
143
+ })
96
144
  }
97
145
 
98
146
  /**
@@ -89,6 +89,19 @@ export async function markThreadAsRead(threadId: number, brand: string): Promise
89
89
  return httpClient.put<void>(`${baseUrl}/v1/threads/${threadId}/read?brand=${brand}`, {})
90
90
  }
91
91
 
92
+ /**
93
+ * Fetches a single forum thread by ID.
94
+ *
95
+ * @param {number} threadId - The ID of the thread to fetch.
96
+ * @param {string} brand - The brand context (e.g., "drumeo", "singeo").
97
+ * @returns {Promise<ForumThread>} - A promise that resolves to the forum thread.
98
+ * @throws {HttpError} - If the HTTP request fails.
99
+ */
100
+ export async function fetchThread(threadId: number, brand: string): Promise<ForumThread> {
101
+ const httpClient = new HttpClient(globalConfig.baseUrl)
102
+ return httpClient.get<ForumThread>(`${baseUrl}/v1/threads/${threadId}?brand=${brand}`)
103
+ }
104
+
92
105
  export interface FetchThreadParams {
93
106
  is_followed?: boolean,
94
107
  page?: number,
File without changes
File without changes
File without changes
@@ -249,8 +249,38 @@ export async function restoreComment(commentId) {
249
249
  * @returns {Promise<*|null>}
250
250
  */
251
251
  export async function replyToComment(commentId, comment) {
252
+ const { generateCommentUrl } = await import('./urlBuilder.ts')
253
+ const { fetchByRailContentIds } = await import('./sanity.js')
254
+
255
+ // Fetch parent comment to get content info
256
+ const parentComment = await fetchComment(commentId)
257
+
258
+ if (!parentComment?.content) {
259
+ const url = `/api/content/v1/comments/${commentId}/reply`
260
+ return await POST(url, { comment })
261
+ }
262
+
263
+ // Fetch content from Sanity to get parentId and correct type
264
+ const contents = await fetchByRailContentIds([parentComment.content.id])
265
+ const content = contents?.[0]
266
+
267
+ // Generate content URL
268
+ const contentUrl = content ? generateCommentUrl({
269
+ id: commentId,
270
+ content: {
271
+ id: content.id,
272
+ type: content.type,
273
+ parentId: content.parentId || content.parent_id,
274
+ brand: content.brand
275
+ }
276
+ }, false) : null
277
+
278
+ const data = {
279
+ comment: comment,
280
+ ...(contentUrl && { content_url: contentUrl })
281
+ }
252
282
  const url = `/api/content/v1/comments/${commentId}/reply`
253
- return await POST(url, { comment })
283
+ return await POST(url, data)
254
284
  }
255
285
 
256
286
  /**
@@ -259,8 +289,28 @@ export async function replyToComment(commentId, comment) {
259
289
  * @returns {Promise<*|null>}
260
290
  */
261
291
  export async function createComment(railcontentId, comment) {
292
+ const { generateContentUrl } = await import('./urlBuilder.ts')
293
+ const { fetchByRailContentIds } = await import('./sanity.js')
294
+
295
+ // Fetch content to get type and brand info
296
+ const contents = await fetchByRailContentIds([railcontentId])
297
+ const content = contents?.[0]
298
+
299
+ // Generate content URL
300
+ const contentUrl = content ? generateContentUrl({
301
+ id: content.id,
302
+ type: content.type,
303
+ parentId: content.parentId || content.parent_id,
304
+ brand: content.brand
305
+ }) : null
306
+
307
+ const data = {
308
+ comment: comment,
309
+ content_id: railcontentId,
310
+ ...(contentUrl && { content_url: contentUrl })
311
+ }
262
312
  const url = `/api/content/v1/comments/store`
263
- return await POST(url, { comment, content_id: railcontentId })
313
+ return await POST(url, data)
264
314
  }
265
315
 
266
316
  /**
@@ -286,8 +336,35 @@ export async function unassignModeratorToComment(commentId) {
286
336
  * @returns {Promise<*|null>}
287
337
  */
288
338
  export async function likeComment(commentId) {
339
+ const { generateCommentUrl } = await import('./urlBuilder.ts')
340
+ const { fetchByRailContentIds } = await import('./sanity.js')
341
+
342
+ // Fetch comment to get content info
343
+ const comment = await fetchComment(commentId)
344
+
345
+ if (!comment?.content) {
346
+ const url = `/api/content/v1/comments/${commentId}/like`
347
+ return await POST(url, null)
348
+ }
349
+
350
+ // Fetch content from Sanity to get parentId and correct type
351
+ const contents = await fetchByRailContentIds([comment.content.id])
352
+ const content = contents?.[0]
353
+
354
+ // Generate content URL
355
+ const contentUrl = content ? generateCommentUrl({
356
+ id: commentId,
357
+ content: {
358
+ id: content.id,
359
+ type: content.type,
360
+ parentId: content.parentId || content.parent_id,
361
+ brand: content.brand
362
+ }
363
+ }, false) : null
364
+
289
365
  const url = `/api/content/v1/comments/${commentId}/like`
290
- return await POST(url, null)
366
+ const data = contentUrl ? { content_url: contentUrl } : {}
367
+ return await POST(url, data)
291
368
  }
292
369
 
293
370
  /**
File without changes