@financial-times/qanda-ui 0.0.1-beta.33 → 0.0.1-beta.35

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.
@@ -6,8 +6,18 @@ const tracking_1 = require("../../utils/tracking");
6
6
  function formatDate(date) {
7
7
  return date.replace(/-|:|\.\d+/g, '');
8
8
  }
9
- const CalendarLink = ({ href, children, }) => {
10
- return ((0, jsx_runtime_1.jsx)("a", { href: href, target: "_blank", rel: "noopener noreferrer", role: "menuitem", "data-trackable": (0, tracking_1.trackingNS)('calendar-link'), className: "live-qa-calendar-list__item__link", children: children }));
9
+ const CalendarLink = ({ href, isDownload = false, children, }) => {
10
+ const attirbutes = isDownload
11
+ ? {
12
+ download: 'event.ics',
13
+ 'data-trackable': (0, tracking_1.trackingNS)('calendar-link-download'),
14
+ }
15
+ : {
16
+ target: '_blank',
17
+ rel: 'noopener noreferrer',
18
+ 'data-trackable': (0, tracking_1.trackingNS)('calendar-link'),
19
+ };
20
+ return ((0, jsx_runtime_1.jsx)("a", { href: href, ...attirbutes, role: "menuitem", className: "live-qa-calendar-list__item__link", children: children }));
11
21
  };
12
22
  function itemChildren(name, icon) {
13
23
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "live-qa-calendar-list__item__icon", children: icon }), (0, jsx_runtime_1.jsx)("span", { className: "live-qa-calendar-list__item__text", children: name })] }));
@@ -30,6 +40,7 @@ const apps = [
30
40
  getLink: ({ storyId, useStaging, commentsAPIUrl }) => {
31
41
  return `${commentsAPIUrl}/story/${storyId}/invitation${useStaging ? '?staging=1' : ''}`;
32
42
  },
43
+ isDownload: true,
33
44
  },
34
45
  {
35
46
  name: 'Yahoo',
@@ -37,9 +48,10 @@ const apps = [
37
48
  getLink: ({ title, startDateTime, endDateTime, storyId }) => `https://calendar.yahoo.com/?v=60&title=${encodeURIComponent(title)}&st=${formatDate(startDateTime)}&et=${formatDate(endDateTime)}&desc=${encodeURIComponent(description(storyId))}&in_loc=${encodeURIComponent(storyId)}`,
38
49
  },
39
50
  ];
40
- exports.default = apps.map(({ name, icon, getLink }) => ({
51
+ exports.default = apps.map(({ name, icon, getLink, isDownload }) => ({
41
52
  name,
42
53
  icon,
54
+ isDownload,
43
55
  link: ({ title, startDateTime, endDateTime, storyId, useStaging, commentsAPIUrl, }) => {
44
56
  const href = getLink({
45
57
  title,
@@ -49,6 +61,6 @@ exports.default = apps.map(({ name, icon, getLink }) => ({
49
61
  useStaging,
50
62
  commentsAPIUrl,
51
63
  });
52
- return (0, jsx_runtime_1.jsx)(CalendarLink, { href: href, children: itemChildren(name, icon) });
64
+ return ((0, jsx_runtime_1.jsx)(CalendarLink, { href: href, isDownload: isDownload, children: itemChildren(name, icon) }));
53
65
  },
54
66
  }));
@@ -48,18 +48,16 @@ exports.nextCommentsApi = (0, react_1.createApi)({
48
48
  console.log(`Connected to SSE: ${sseEndpoint}`);
49
49
  const state = getState();
50
50
  const lastEventId = state.stream.mostRecentUpdateTime;
51
- if (lastEventId !== null && lastEventId !== undefined) {
52
- try {
53
- await dispatch(exports.nextCommentsApi.endpoints.getCatchupUpdates.initiate({
54
- storyId: arg.storyId,
55
- useStaging: arg.useStaging,
56
- commentsAPIUrl: arg.commentsAPIUrl,
57
- lastEventId: `${lastEventId}`,
58
- }));
59
- }
60
- catch (error) {
61
- reliability_1.qandaReliability.failure('qa-updates', error);
62
- }
51
+ try {
52
+ await dispatch(exports.nextCommentsApi.endpoints.getCatchupUpdates.initiate({
53
+ storyId: arg.storyId,
54
+ useStaging: arg.useStaging,
55
+ commentsAPIUrl: arg.commentsAPIUrl,
56
+ ...(lastEventId && { lastEventId: `${lastEventId}` }),
57
+ }, { forceRefetch: true }));
58
+ }
59
+ catch (error) {
60
+ reliability_1.qandaReliability.failure('qa-updates', error);
63
61
  }
64
62
  };
65
63
  exports.eventSource.onmessage = (event) => {
@@ -117,14 +115,27 @@ exports.nextCommentsApi = (0, react_1.createApi)({
117
115
  }),
118
116
  // CATCHUP UPDATES
119
117
  getCatchupUpdates: builder.query({
120
- query: ({ storyId, useStaging, commentsAPIUrl, lastEventId, }) => `${commentsAPIUrl}/story/${storyId}/catchup-updates?${useStaging ? 'staging=1&' : ''}${`lastEventId=${lastEventId}`}`,
118
+ query: ({ storyId, useStaging, commentsAPIUrl, lastEventId, }) => {
119
+ const params = new URLSearchParams();
120
+ if (useStaging) {
121
+ params.append('staging', '1');
122
+ }
123
+ if (lastEventId) {
124
+ params.append('lastEventId', lastEventId);
125
+ }
126
+ const queryString = params.toString();
127
+ return `${commentsAPIUrl}/story/${storyId}/catchup-updates${queryString ? `?${queryString}` : ''}`;
128
+ },
121
129
  transformResponse: (response) => {
122
- const updatedComments = response?.comments.map((comment) => {
123
- return comment.data;
124
- });
130
+ const updatedComments = Array.isArray(response?.comments)
131
+ ? response.comments.map((comment) => {
132
+ return comment.data;
133
+ })
134
+ : [];
125
135
  return {
126
136
  updatedComments,
127
- lastEventId: response?.lastEventId,
137
+ status: response?.status ?? null,
138
+ lastEventId: response?.lastEventId ?? null,
128
139
  };
129
140
  },
130
141
  }),
@@ -18,7 +18,7 @@ exports.listenerMiddleware = (0, toolkit_1.createListenerMiddleware)();
18
18
  * Calculates the most recent update timestamp and answer id from a Q&A question
19
19
  *
20
20
  * @param qandas - A Comment object representing a question with nested answers
21
- * @returns Object containing mostRecentUpdateTime (timestamp) and mostRecentUpdateId (answer id)
21
+ * @returns Object containing mostRecentUpdateTime (answer timestamp) and mostRecentUpdateId (answer id)
22
22
  *
23
23
  * @example
24
24
  * const { mostRecentUpdateTime, mostRecentUpdateId } = getMostRecentUpdate(qandas);
@@ -38,6 +38,28 @@ function getMostRecentUpdate(qandaComment) {
38
38
  }
39
39
  return { mostRecentUpdateTime, mostRecentUpdateId };
40
40
  }
41
+ /**
42
+ * Shallow compares two StreamStatus objects to determine if they are identical
43
+ *
44
+ * Performs a shallow comparison of all properties between the previous and new status
45
+ * entries. Used to prevent unnecessary updates when the status hasn't actually changed.
46
+ *
47
+ * @param previousEntry - The previous StreamStatus object to compare
48
+ * @param newEntry - The new StreamStatus object to compare against
49
+ * @returns True if all properties are identical, false otherwise
50
+ */
51
+ function isSameStatus(previousEntry, newEntry) {
52
+ if (Object.keys(previousEntry).length !== Object.keys(newEntry).length) {
53
+ return false;
54
+ }
55
+ for (const key in previousEntry) {
56
+ const typedKey = key;
57
+ if (previousEntry[typedKey] !== newEntry[typedKey]) {
58
+ return false;
59
+ }
60
+ }
61
+ return true;
62
+ }
41
63
  /**
42
64
  * Listener: Updates most recent timestamp and initiates catchup when live Q&A stream data is fetched
43
65
  *
@@ -51,12 +73,17 @@ function getMostRecentUpdate(qandaComment) {
51
73
  * @dispatches setStreamStartMostRecentAnswerId - Updates stream.streamStartMostRecentAnswerId in Redux store
52
74
  * @dispatches nextCommentsApi.endpoints.getCatchupUpdates.initiate - Triggers catchup request for missed updates
53
75
  */
76
+ // TODO: consider when this should run now that we have state changes too.
77
+ // consider that it will run on every live stream query, not just the first one.
78
+ // therefore it will happen on state change, and on first load maybe twice.
54
79
  exports.listenerMiddleware.startListening({
55
80
  matcher: (0, toolkit_1.isAnyOf)(comments_api_1.nextCommentsApi.endpoints.getQandAStream.matchFulfilled),
56
81
  effect: (action, listenerApi) => {
82
+ const state = listenerApi.getState();
57
83
  const { payload, meta } = action;
58
84
  const { storyId, useStaging, commentsAPIUrl, status } = meta.arg.originalArgs;
59
- // Check if this is a live stream QUERY, not response. If it is a live query we get questions and answers.
85
+ // Check if this is a live stream QUERY, not response. If it is a live QUERY we will get questions and answers. This will fire when stream starts in live mode or switches to live mode for first time.
86
+ const isPreLiveStream = status === 'pre-live';
60
87
  const isLiveStream = status === 'live';
61
88
  if (isLiveStream &&
62
89
  Array.isArray(payload.qandas) &&
@@ -68,54 +95,76 @@ exports.listenerMiddleware.startListening({
68
95
  listenerApi.dispatch((0, stream_1.setMostRecentUpdateTime)(`${mostRecentUpdateTime}`));
69
96
  listenerApi.dispatch((0, stream_1.setStreamLoadMostRecentAnswerId)(mostRecentUpdateId));
70
97
  }
98
+ }
99
+ const { mostRecentUpdateTime } = state.stream;
100
+ if (isPreLiveStream || isLiveStream) {
71
101
  listenerApi.dispatch(comments_api_1.nextCommentsApi.endpoints.getCatchupUpdates.initiate({
72
- storyId: storyId,
73
- useStaging: useStaging,
74
- commentsAPIUrl: commentsAPIUrl,
75
- lastEventId: `${mostRecentUpdateTime}`,
102
+ storyId,
103
+ useStaging,
104
+ commentsAPIUrl,
105
+ ...(mostRecentUpdateTime && {
106
+ lastEventId: `${mostRecentUpdateTime}`,
107
+ }),
76
108
  }));
77
109
  }
78
110
  },
79
111
  });
80
112
  /**
81
- * Listener: Updates updates cache and most recent update when catchup comments are received
113
+ * Listener: Processes successful catchup updates and manages comment/status updates
82
114
  *
83
- * Responds to successful getCatchupUpdates queries and adds them to the updates list.
84
- * Filters out comments that match the most recent update from the stream to avoid
85
- * duplicates caused by latency between coral and valkey stream updates.
115
+ * Responds to successful getCatchupUpdates queries and processes the response:
116
+ * - Adds new comments to the updates cache, filtering out duplicates
117
+ * - Updates status when changes are detected compared to the last status
118
+ * - Updates the most recent timestamp for future catchup requests
86
119
  *
87
120
  * @listens nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled
88
121
  * @dispatches nextCommentsApi.util.updateQueryData - Updates the cache of updated comments in the store
89
122
  * @dispatches setMostRecentUpdateTime - Updates stream.mostRecentUpdateTime with new lastEventId
123
+ * @dispatches nextCommentsApi.util.updateQueryData - Updates the status if it has changed
90
124
  */
91
125
  exports.listenerMiddleware.startListening({
92
126
  matcher: comments_api_1.nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled,
93
127
  effect: (action, listenerApi) => {
94
128
  const { payload, meta } = action;
95
129
  const { storyId, useStaging, commentsAPIUrl } = meta.arg.originalArgs;
96
- const { updatedComments, lastEventId } = payload;
97
- if (!updatedComments)
130
+ const { updatedComments, status, lastEventId = null } = payload;
131
+ if (!updatedComments && !status)
98
132
  return;
99
- console.log('Received catchup updates, new event id:', lastEventId);
100
- const updateType = comments_api_1.updateTypes['QA_NEW_REPLY'];
101
133
  const state = listenerApi.getState();
102
- const { streamLoadMostRecentAnswerId, mostRecentUpdateTime: mostRecentInStreamTime, } = state.stream;
103
- // filter out catchup comments that are the same as the most recent update in the stream list, which can happen on first load
104
- // there is latency between the comment being saved in coral and then added to our valkey stream. Updates after this will all come from valkey.
105
- const filteredComments = updatedComments.filter((comment) => {
106
- const { mostRecentUpdateId, mostRecentUpdateTime } = getMostRecentUpdate(comment);
107
- if (streamLoadMostRecentAnswerId === mostRecentUpdateId &&
108
- mostRecentInStreamTime === `${mostRecentUpdateTime}`) {
109
- return false;
134
+ // check for new updated comments
135
+ if (Array.isArray(updatedComments) && updatedComments.length > 0) {
136
+ const updateType = comments_api_1.updateTypes['QA_NEW_REPLY'];
137
+ const { streamLoadMostRecentAnswerId, mostRecentUpdateTime: mostRecentInStreamTime, } = state.stream;
138
+ // filter out catchup comments that are the same as the most recent update in the stream list, which can happen on first load when the valkey and coral response timestamps don't match
139
+ // there is latency between the comment being saved in coral and then added to our valkey stream. Updates after this will all come from valkey.
140
+ const filteredComments = updatedComments.filter((comment) => {
141
+ const { mostRecentUpdateId, mostRecentUpdateTime } = getMostRecentUpdate(comment);
142
+ if (streamLoadMostRecentAnswerId === mostRecentUpdateId &&
143
+ mostRecentInStreamTime === `${mostRecentUpdateTime}`) {
144
+ return false;
145
+ }
146
+ return true;
147
+ });
148
+ if (Array.isArray(filteredComments) && filteredComments.length) {
149
+ listenerApi.dispatch(comments_api_1.nextCommentsApi.util.updateQueryData('getQandAUpdates', { storyId, useStaging, commentsAPIUrl }, (draft) => {
150
+ draft[updateType] = draft[updateType] || [];
151
+ draft[updateType].push(...filteredComments);
152
+ }));
110
153
  }
111
- return true;
112
- });
113
- if (Array.isArray(filteredComments) && filteredComments.length) {
154
+ listenerApi.dispatch((0, stream_1.setMostRecentUpdateTime)(lastEventId));
155
+ }
156
+ // check for status updates
157
+ if (status) {
158
+ const updateType = comments_api_1.updateTypes['QA_UPDATE'];
114
159
  listenerApi.dispatch(comments_api_1.nextCommentsApi.util.updateQueryData('getQandAUpdates', { storyId, useStaging, commentsAPIUrl }, (draft) => {
115
160
  draft[updateType] = draft[updateType] || [];
116
- draft[updateType].push(...filteredComments);
161
+ // in order to avoid unnecessary updates, we check if the last update is identical to the new status. we only push changed status updates.
162
+ if (draft[updateType].length > 0 &&
163
+ isSameStatus(draft[updateType].at(-1), status)) {
164
+ return;
165
+ }
166
+ draft[updateType] = [status];
117
167
  }));
118
168
  }
119
- listenerApi.dispatch((0, stream_1.setMostRecentUpdateTime)(lastEventId));
120
169
  },
121
170
  });
@@ -34,7 +34,7 @@ const updateComments = (dispatch, updatedComments, storyId, useStaging, comments
34
34
  dispatch((0, stream_1.setLatestAnsweredQuestionId)(latestAnsweredQuestionId));
35
35
  }
36
36
  catch (error) {
37
- console.log('error', error);
37
+ console.error('error', error);
38
38
  }
39
39
  };
40
40
  exports.updateComments = updateComments;
@@ -4,8 +4,18 @@ import { trackingNS } from '../../utils/tracking';
4
4
  function formatDate(date) {
5
5
  return date.replace(/-|:|\.\d+/g, '');
6
6
  }
7
- const CalendarLink = ({ href, children, }) => {
8
- return (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", role: "menuitem", "data-trackable": trackingNS('calendar-link'), className: "live-qa-calendar-list__item__link", children: children }));
7
+ const CalendarLink = ({ href, isDownload = false, children, }) => {
8
+ const attirbutes = isDownload
9
+ ? {
10
+ download: 'event.ics',
11
+ 'data-trackable': trackingNS('calendar-link-download'),
12
+ }
13
+ : {
14
+ target: '_blank',
15
+ rel: 'noopener noreferrer',
16
+ 'data-trackable': trackingNS('calendar-link'),
17
+ };
18
+ return (_jsx("a", { href: href, ...attirbutes, role: "menuitem", className: "live-qa-calendar-list__item__link", children: children }));
9
19
  };
10
20
  function itemChildren(name, icon) {
11
21
  return (_jsxs(_Fragment, { children: [_jsx("span", { className: "live-qa-calendar-list__item__icon", children: icon }), _jsx("span", { className: "live-qa-calendar-list__item__text", children: name })] }));
@@ -28,6 +38,7 @@ const apps = [
28
38
  getLink: ({ storyId, useStaging, commentsAPIUrl }) => {
29
39
  return `${commentsAPIUrl}/story/${storyId}/invitation${useStaging ? '?staging=1' : ''}`;
30
40
  },
41
+ isDownload: true,
31
42
  },
32
43
  {
33
44
  name: 'Yahoo',
@@ -35,9 +46,10 @@ const apps = [
35
46
  getLink: ({ title, startDateTime, endDateTime, storyId }) => `https://calendar.yahoo.com/?v=60&title=${encodeURIComponent(title)}&st=${formatDate(startDateTime)}&et=${formatDate(endDateTime)}&desc=${encodeURIComponent(description(storyId))}&in_loc=${encodeURIComponent(storyId)}`,
36
47
  },
37
48
  ];
38
- export default apps.map(({ name, icon, getLink }) => ({
49
+ export default apps.map(({ name, icon, getLink, isDownload }) => ({
39
50
  name,
40
51
  icon,
52
+ isDownload,
41
53
  link: ({ title, startDateTime, endDateTime, storyId, useStaging, commentsAPIUrl, }) => {
42
54
  const href = getLink({
43
55
  title,
@@ -47,6 +59,6 @@ export default apps.map(({ name, icon, getLink }) => ({
47
59
  useStaging,
48
60
  commentsAPIUrl,
49
61
  });
50
- return _jsx(CalendarLink, { href: href, children: itemChildren(name, icon) });
62
+ return (_jsx(CalendarLink, { href: href, isDownload: isDownload, children: itemChildren(name, icon) }));
51
63
  },
52
64
  }));
@@ -45,18 +45,16 @@ export const nextCommentsApi = createApi({
45
45
  console.log(`Connected to SSE: ${sseEndpoint}`);
46
46
  const state = getState();
47
47
  const lastEventId = state.stream.mostRecentUpdateTime;
48
- if (lastEventId !== null && lastEventId !== undefined) {
49
- try {
50
- await dispatch(nextCommentsApi.endpoints.getCatchupUpdates.initiate({
51
- storyId: arg.storyId,
52
- useStaging: arg.useStaging,
53
- commentsAPIUrl: arg.commentsAPIUrl,
54
- lastEventId: `${lastEventId}`,
55
- }));
56
- }
57
- catch (error) {
58
- qandaReliability.failure('qa-updates', error);
59
- }
48
+ try {
49
+ await dispatch(nextCommentsApi.endpoints.getCatchupUpdates.initiate({
50
+ storyId: arg.storyId,
51
+ useStaging: arg.useStaging,
52
+ commentsAPIUrl: arg.commentsAPIUrl,
53
+ ...(lastEventId && { lastEventId: `${lastEventId}` }),
54
+ }, { forceRefetch: true }));
55
+ }
56
+ catch (error) {
57
+ qandaReliability.failure('qa-updates', error);
60
58
  }
61
59
  };
62
60
  eventSource.onmessage = (event) => {
@@ -114,14 +112,27 @@ export const nextCommentsApi = createApi({
114
112
  }),
115
113
  // CATCHUP UPDATES
116
114
  getCatchupUpdates: builder.query({
117
- query: ({ storyId, useStaging, commentsAPIUrl, lastEventId, }) => `${commentsAPIUrl}/story/${storyId}/catchup-updates?${useStaging ? 'staging=1&' : ''}${`lastEventId=${lastEventId}`}`,
115
+ query: ({ storyId, useStaging, commentsAPIUrl, lastEventId, }) => {
116
+ const params = new URLSearchParams();
117
+ if (useStaging) {
118
+ params.append('staging', '1');
119
+ }
120
+ if (lastEventId) {
121
+ params.append('lastEventId', lastEventId);
122
+ }
123
+ const queryString = params.toString();
124
+ return `${commentsAPIUrl}/story/${storyId}/catchup-updates${queryString ? `?${queryString}` : ''}`;
125
+ },
118
126
  transformResponse: (response) => {
119
- const updatedComments = response?.comments.map((comment) => {
120
- return comment.data;
121
- });
127
+ const updatedComments = Array.isArray(response?.comments)
128
+ ? response.comments.map((comment) => {
129
+ return comment.data;
130
+ })
131
+ : [];
122
132
  return {
123
133
  updatedComments,
124
- lastEventId: response?.lastEventId,
134
+ status: response?.status ?? null,
135
+ lastEventId: response?.lastEventId ?? null,
125
136
  };
126
137
  },
127
138
  }),
@@ -15,7 +15,7 @@ export const listenerMiddleware = createListenerMiddleware();
15
15
  * Calculates the most recent update timestamp and answer id from a Q&A question
16
16
  *
17
17
  * @param qandas - A Comment object representing a question with nested answers
18
- * @returns Object containing mostRecentUpdateTime (timestamp) and mostRecentUpdateId (answer id)
18
+ * @returns Object containing mostRecentUpdateTime (answer timestamp) and mostRecentUpdateId (answer id)
19
19
  *
20
20
  * @example
21
21
  * const { mostRecentUpdateTime, mostRecentUpdateId } = getMostRecentUpdate(qandas);
@@ -35,6 +35,28 @@ function getMostRecentUpdate(qandaComment) {
35
35
  }
36
36
  return { mostRecentUpdateTime, mostRecentUpdateId };
37
37
  }
38
+ /**
39
+ * Shallow compares two StreamStatus objects to determine if they are identical
40
+ *
41
+ * Performs a shallow comparison of all properties between the previous and new status
42
+ * entries. Used to prevent unnecessary updates when the status hasn't actually changed.
43
+ *
44
+ * @param previousEntry - The previous StreamStatus object to compare
45
+ * @param newEntry - The new StreamStatus object to compare against
46
+ * @returns True if all properties are identical, false otherwise
47
+ */
48
+ function isSameStatus(previousEntry, newEntry) {
49
+ if (Object.keys(previousEntry).length !== Object.keys(newEntry).length) {
50
+ return false;
51
+ }
52
+ for (const key in previousEntry) {
53
+ const typedKey = key;
54
+ if (previousEntry[typedKey] !== newEntry[typedKey]) {
55
+ return false;
56
+ }
57
+ }
58
+ return true;
59
+ }
38
60
  /**
39
61
  * Listener: Updates most recent timestamp and initiates catchup when live Q&A stream data is fetched
40
62
  *
@@ -48,12 +70,17 @@ function getMostRecentUpdate(qandaComment) {
48
70
  * @dispatches setStreamStartMostRecentAnswerId - Updates stream.streamStartMostRecentAnswerId in Redux store
49
71
  * @dispatches nextCommentsApi.endpoints.getCatchupUpdates.initiate - Triggers catchup request for missed updates
50
72
  */
73
+ // TODO: consider when this should run now that we have state changes too.
74
+ // consider that it will run on every live stream query, not just the first one.
75
+ // therefore it will happen on state change, and on first load maybe twice.
51
76
  listenerMiddleware.startListening({
52
77
  matcher: isAnyOf(nextCommentsApi.endpoints.getQandAStream.matchFulfilled),
53
78
  effect: (action, listenerApi) => {
79
+ const state = listenerApi.getState();
54
80
  const { payload, meta } = action;
55
81
  const { storyId, useStaging, commentsAPIUrl, status } = meta.arg.originalArgs;
56
- // Check if this is a live stream QUERY, not response. If it is a live query we get questions and answers.
82
+ // Check if this is a live stream QUERY, not response. If it is a live QUERY we will get questions and answers. This will fire when stream starts in live mode or switches to live mode for first time.
83
+ const isPreLiveStream = status === 'pre-live';
57
84
  const isLiveStream = status === 'live';
58
85
  if (isLiveStream &&
59
86
  Array.isArray(payload.qandas) &&
@@ -65,54 +92,76 @@ listenerMiddleware.startListening({
65
92
  listenerApi.dispatch(setMostRecentUpdateTime(`${mostRecentUpdateTime}`));
66
93
  listenerApi.dispatch(setStreamLoadMostRecentAnswerId(mostRecentUpdateId));
67
94
  }
95
+ }
96
+ const { mostRecentUpdateTime } = state.stream;
97
+ if (isPreLiveStream || isLiveStream) {
68
98
  listenerApi.dispatch(nextCommentsApi.endpoints.getCatchupUpdates.initiate({
69
- storyId: storyId,
70
- useStaging: useStaging,
71
- commentsAPIUrl: commentsAPIUrl,
72
- lastEventId: `${mostRecentUpdateTime}`,
99
+ storyId,
100
+ useStaging,
101
+ commentsAPIUrl,
102
+ ...(mostRecentUpdateTime && {
103
+ lastEventId: `${mostRecentUpdateTime}`,
104
+ }),
73
105
  }));
74
106
  }
75
107
  },
76
108
  });
77
109
  /**
78
- * Listener: Updates updates cache and most recent update when catchup comments are received
110
+ * Listener: Processes successful catchup updates and manages comment/status updates
79
111
  *
80
- * Responds to successful getCatchupUpdates queries and adds them to the updates list.
81
- * Filters out comments that match the most recent update from the stream to avoid
82
- * duplicates caused by latency between coral and valkey stream updates.
112
+ * Responds to successful getCatchupUpdates queries and processes the response:
113
+ * - Adds new comments to the updates cache, filtering out duplicates
114
+ * - Updates status when changes are detected compared to the last status
115
+ * - Updates the most recent timestamp for future catchup requests
83
116
  *
84
117
  * @listens nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled
85
118
  * @dispatches nextCommentsApi.util.updateQueryData - Updates the cache of updated comments in the store
86
119
  * @dispatches setMostRecentUpdateTime - Updates stream.mostRecentUpdateTime with new lastEventId
120
+ * @dispatches nextCommentsApi.util.updateQueryData - Updates the status if it has changed
87
121
  */
88
122
  listenerMiddleware.startListening({
89
123
  matcher: nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled,
90
124
  effect: (action, listenerApi) => {
91
125
  const { payload, meta } = action;
92
126
  const { storyId, useStaging, commentsAPIUrl } = meta.arg.originalArgs;
93
- const { updatedComments, lastEventId } = payload;
94
- if (!updatedComments)
127
+ const { updatedComments, status, lastEventId = null } = payload;
128
+ if (!updatedComments && !status)
95
129
  return;
96
- console.log('Received catchup updates, new event id:', lastEventId);
97
- const updateType = updateTypes['QA_NEW_REPLY'];
98
130
  const state = listenerApi.getState();
99
- const { streamLoadMostRecentAnswerId, mostRecentUpdateTime: mostRecentInStreamTime, } = state.stream;
100
- // filter out catchup comments that are the same as the most recent update in the stream list, which can happen on first load
101
- // there is latency between the comment being saved in coral and then added to our valkey stream. Updates after this will all come from valkey.
102
- const filteredComments = updatedComments.filter((comment) => {
103
- const { mostRecentUpdateId, mostRecentUpdateTime } = getMostRecentUpdate(comment);
104
- if (streamLoadMostRecentAnswerId === mostRecentUpdateId &&
105
- mostRecentInStreamTime === `${mostRecentUpdateTime}`) {
106
- return false;
131
+ // check for new updated comments
132
+ if (Array.isArray(updatedComments) && updatedComments.length > 0) {
133
+ const updateType = updateTypes['QA_NEW_REPLY'];
134
+ const { streamLoadMostRecentAnswerId, mostRecentUpdateTime: mostRecentInStreamTime, } = state.stream;
135
+ // filter out catchup comments that are the same as the most recent update in the stream list, which can happen on first load when the valkey and coral response timestamps don't match
136
+ // there is latency between the comment being saved in coral and then added to our valkey stream. Updates after this will all come from valkey.
137
+ const filteredComments = updatedComments.filter((comment) => {
138
+ const { mostRecentUpdateId, mostRecentUpdateTime } = getMostRecentUpdate(comment);
139
+ if (streamLoadMostRecentAnswerId === mostRecentUpdateId &&
140
+ mostRecentInStreamTime === `${mostRecentUpdateTime}`) {
141
+ return false;
142
+ }
143
+ return true;
144
+ });
145
+ if (Array.isArray(filteredComments) && filteredComments.length) {
146
+ listenerApi.dispatch(nextCommentsApi.util.updateQueryData('getQandAUpdates', { storyId, useStaging, commentsAPIUrl }, (draft) => {
147
+ draft[updateType] = draft[updateType] || [];
148
+ draft[updateType].push(...filteredComments);
149
+ }));
107
150
  }
108
- return true;
109
- });
110
- if (Array.isArray(filteredComments) && filteredComments.length) {
151
+ listenerApi.dispatch(setMostRecentUpdateTime(lastEventId));
152
+ }
153
+ // check for status updates
154
+ if (status) {
155
+ const updateType = updateTypes['QA_UPDATE'];
111
156
  listenerApi.dispatch(nextCommentsApi.util.updateQueryData('getQandAUpdates', { storyId, useStaging, commentsAPIUrl }, (draft) => {
112
157
  draft[updateType] = draft[updateType] || [];
113
- draft[updateType].push(...filteredComments);
158
+ // in order to avoid unnecessary updates, we check if the last update is identical to the new status. we only push changed status updates.
159
+ if (draft[updateType].length > 0 &&
160
+ isSameStatus(draft[updateType].at(-1), status)) {
161
+ return;
162
+ }
163
+ draft[updateType] = [status];
114
164
  }));
115
165
  }
116
- listenerApi.dispatch(setMostRecentUpdateTime(lastEventId));
117
166
  },
118
167
  });
@@ -31,6 +31,6 @@ export const updateComments = (dispatch, updatedComments, storyId, useStaging, c
31
31
  dispatch(setLatestAnsweredQuestionId(latestAnsweredQuestionId));
32
32
  }
33
33
  catch (error) {
34
- console.log('error', error);
34
+ console.error('error', error);
35
35
  }
36
36
  };
@@ -6,11 +6,13 @@ export type LinkProps = {
6
6
  storyId: string;
7
7
  useStaging: boolean;
8
8
  commentsAPIUrl: string;
9
+ isDownload?: boolean;
9
10
  children?: string;
10
11
  };
11
12
  declare const _default: {
12
13
  name: string;
13
14
  icon: JSX.Element;
15
+ isDownload: boolean | undefined;
14
16
  link: ({ title, startDateTime, endDateTime, storyId, useStaging, commentsAPIUrl, }: PropsWithChildren<LinkProps>) => JSX.Element;
15
17
  }[];
16
18
  export default _default;
@@ -1,51 +1,21 @@
1
- import type { Comment } from '../types/comment';
2
- import type { QA } from '../types/qa';
3
1
  import type { Status } from '../types/qa';
2
+ import type { CommentsAPIUrlParams, CommentsStreamTransformed, CachedData, UserRolesTransformed, CommentsAPIUrlTokenParams, CommentsAPIUrlQuestionParams } from '../types/comments-api';
4
3
  export declare const updateTypes: {
5
4
  QA_NEW_REPLY: string;
6
5
  QA_UPDATE: string;
7
6
  };
8
7
  export declare let eventSource: EventSource | null;
9
- type CommentsStreamResponse = {
10
- type: string;
11
- children?: Comment[];
12
- status?: Status;
13
- startDateTime?: string;
14
- endDateTime?: string;
15
- };
16
- type CommentsStreamTransformed = Omit<CommentsStreamResponse, 'children'> & {
17
- qandas: Comment[];
18
- };
19
- type CommentsAPIUrlParams = {
20
- storyId: string;
21
- useStaging: boolean;
22
- commentsAPIUrl: string;
23
- };
24
- type CommentsAPIUrlTokenParams = CommentsAPIUrlParams & {
25
- token: string;
26
- };
27
- type CommentsAPIUrlQuestionParams = CommentsAPIUrlTokenParams & {
28
- question: string;
29
- postAnonymously: boolean;
30
- };
31
- type CachedData = {
32
- updatedComments?: Comment[];
33
- updateQA?: QA;
34
- [key: string]: any;
35
- };
36
- type UserRolesTransformed = {
37
- expertView: boolean;
38
- };
39
8
  export declare const nextCommentsApi: import("@reduxjs/toolkit/query/react").Api<import("@reduxjs/toolkit/query/react").BaseQueryFn<string | import("@reduxjs/toolkit/query/react").FetchArgs, unknown, import("@reduxjs/toolkit/query/react").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query/react").FetchBaseQueryMeta>, {
40
9
  getQandAStream: import("@reduxjs/toolkit/query/react").QueryDefinition<CommentsAPIUrlParams & {
41
10
  status: Status;
42
11
  }, import("@reduxjs/toolkit/query/react").BaseQueryFn<string | import("@reduxjs/toolkit/query/react").FetchArgs, unknown, import("@reduxjs/toolkit/query/react").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query/react").FetchBaseQueryMeta>, never, CommentsStreamTransformed, "nextCommentsApi">;
43
12
  getQandAUpdates: import("@reduxjs/toolkit/query/react").QueryDefinition<CommentsAPIUrlParams, import("@reduxjs/toolkit/query/react").BaseQueryFn<string | import("@reduxjs/toolkit/query/react").FetchArgs, unknown, import("@reduxjs/toolkit/query/react").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query/react").FetchBaseQueryMeta>, never, CachedData, "nextCommentsApi">;
44
13
  getCatchupUpdates: import("@reduxjs/toolkit/query/react").QueryDefinition<CommentsAPIUrlParams & {
45
- lastEventId: string;
14
+ lastEventId?: string;
46
15
  }, import("@reduxjs/toolkit/query/react").BaseQueryFn<string | import("@reduxjs/toolkit/query/react").FetchArgs, unknown, import("@reduxjs/toolkit/query/react").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query/react").FetchBaseQueryMeta>, never, {
47
- updatedComments: Comment[] | undefined;
48
- lastEventId: string | undefined;
16
+ updatedComments: import("../types/comment").Comment[];
17
+ status: import("../types/comments-api").StreamStatus | null;
18
+ lastEventId: string | null;
49
19
  }, "nextCommentsApi">;
50
20
  getUserRoles: import("@reduxjs/toolkit/query/react").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query/react").BaseQueryFn<string | import("@reduxjs/toolkit/query/react").FetchArgs, unknown, import("@reduxjs/toolkit/query/react").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query/react").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">;
51
21
  postQuestion: import("@reduxjs/toolkit/query/react").MutationDefinition<CommentsAPIUrlQuestionParams, import("@reduxjs/toolkit/query/react").BaseQueryFn<string | import("@reduxjs/toolkit/query/react").FetchArgs, unknown, import("@reduxjs/toolkit/query/react").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query/react").FetchBaseQueryMeta>, never, {
@@ -59,6 +29,5 @@ export declare const useGetQandAStreamQuery: import("@reduxjs/toolkit/dist/query
59
29
  }, "nextCommentsApi">>;
60
30
  export declare const useUpdatedComments: ({ storyId, useStaging, commentsAPIUrl }: CommentsAPIUrlParams, options?: {
61
31
  skip: boolean;
62
- }) => Comment[];
32
+ }) => import("../types/comment").Comment[];
63
33
  export declare const useUpdatedQA: ({ storyId, useStaging, commentsAPIUrl, }: CommentsAPIUrlParams) => any;
64
- export {};
@@ -9,59 +9,19 @@ export declare const rootReducer: import("@reduxjs/toolkit").Reducer<import("@re
9
9
  user: any;
10
10
  expert: ExpertState;
11
11
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
12
- getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
13
- storyId: string;
14
- useStaging: boolean;
15
- commentsAPIUrl: string;
16
- } & {
12
+ getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
17
13
  status: import("../types/qa").Status;
18
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, Omit<{
19
- type: string;
20
- children?: import("../types/comment").Comment[];
21
- status?: import("../types/qa").Status;
22
- startDateTime?: string;
23
- endDateTime?: string;
24
- }, "children"> & {
25
- qandas: import("../types/comment").Comment[];
26
- }, "nextCommentsApi">;
27
- getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
28
- storyId: string;
29
- useStaging: boolean;
30
- commentsAPIUrl: string;
31
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
32
- [key: string]: any;
33
- updatedComments?: import("../types/comment").Comment[];
34
- updateQA?: import("../types/qa").QA;
35
- }, "nextCommentsApi">;
36
- getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
37
- storyId: string;
38
- useStaging: boolean;
39
- commentsAPIUrl: string;
40
- } & {
41
- lastEventId: string;
14
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CommentsStreamTransformed, "nextCommentsApi">;
15
+ getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CachedData, "nextCommentsApi">;
16
+ getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
17
+ lastEventId?: string;
42
18
  }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
43
- updatedComments: import("../types/comment").Comment[] | undefined;
44
- lastEventId: string | undefined;
19
+ updatedComments: import("../types/comment").Comment[];
20
+ status: import("../types/comments-api").StreamStatus | null;
21
+ lastEventId: string | null;
45
22
  }, "nextCommentsApi">;
46
- getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<{
47
- storyId: string;
48
- useStaging: boolean;
49
- commentsAPIUrl: string;
50
- } & {
51
- token: string;
52
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
53
- expertView: boolean;
54
- }, "nextCommentsApi">;
55
- postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
56
- storyId: string;
57
- useStaging: boolean;
58
- commentsAPIUrl: string;
59
- } & {
60
- token: string;
61
- } & {
62
- question: string;
63
- postAnonymously: boolean;
64
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
23
+ getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").UserRolesTransformed, "nextCommentsApi">;
24
+ postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<import("../types/comments-api").CommentsAPIUrlQuestionParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
65
25
  message: string;
66
26
  }, "nextCommentsApi">;
67
27
  }, never, "nextCommentsApi">;
@@ -78,59 +38,19 @@ export declare const store: import("@reduxjs/toolkit/dist/configureStore").Toolk
78
38
  user: any;
79
39
  expert: ExpertState;
80
40
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
81
- getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
82
- storyId: string;
83
- useStaging: boolean;
84
- commentsAPIUrl: string;
85
- } & {
41
+ getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
86
42
  status: import("../types/qa").Status;
87
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, Omit<{
88
- type: string;
89
- children?: import("../types/comment").Comment[];
90
- status?: import("../types/qa").Status;
91
- startDateTime?: string;
92
- endDateTime?: string;
93
- }, "children"> & {
94
- qandas: import("../types/comment").Comment[];
95
- }, "nextCommentsApi">;
96
- getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
97
- storyId: string;
98
- useStaging: boolean;
99
- commentsAPIUrl: string;
100
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
101
- [key: string]: any;
102
- updatedComments?: import("../types/comment").Comment[];
103
- updateQA?: import("../types/qa").QA;
104
- }, "nextCommentsApi">;
105
- getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
106
- storyId: string;
107
- useStaging: boolean;
108
- commentsAPIUrl: string;
109
- } & {
110
- lastEventId: string;
43
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CommentsStreamTransformed, "nextCommentsApi">;
44
+ getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CachedData, "nextCommentsApi">;
45
+ getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
46
+ lastEventId?: string;
111
47
  }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
112
- updatedComments: import("../types/comment").Comment[] | undefined;
113
- lastEventId: string | undefined;
48
+ updatedComments: import("../types/comment").Comment[];
49
+ status: import("../types/comments-api").StreamStatus | null;
50
+ lastEventId: string | null;
114
51
  }, "nextCommentsApi">;
115
- getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<{
116
- storyId: string;
117
- useStaging: boolean;
118
- commentsAPIUrl: string;
119
- } & {
120
- token: string;
121
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
122
- expertView: boolean;
123
- }, "nextCommentsApi">;
124
- postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
125
- storyId: string;
126
- useStaging: boolean;
127
- commentsAPIUrl: string;
128
- } & {
129
- token: string;
130
- } & {
131
- question: string;
132
- postAnonymously: boolean;
133
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
52
+ getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").UserRolesTransformed, "nextCommentsApi">;
53
+ postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<import("../types/comments-api").CommentsAPIUrlQuestionParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
134
54
  message: string;
135
55
  }, "nextCommentsApi">;
136
56
  }, never, "nextCommentsApi">;
@@ -140,116 +60,36 @@ export declare const store: import("@reduxjs/toolkit/dist/configureStore").Toolk
140
60
  user: any;
141
61
  expert: ExpertState;
142
62
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
143
- getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
144
- storyId: string;
145
- useStaging: boolean;
146
- commentsAPIUrl: string;
147
- } & {
63
+ getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
148
64
  status: import("../types/qa").Status;
149
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, Omit<{
150
- type: string;
151
- children?: import("../types/comment").Comment[];
152
- status?: import("../types/qa").Status;
153
- startDateTime?: string;
154
- endDateTime?: string;
155
- }, "children"> & {
156
- qandas: import("../types/comment").Comment[];
157
- }, "nextCommentsApi">;
158
- getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
159
- storyId: string;
160
- useStaging: boolean;
161
- commentsAPIUrl: string;
162
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
163
- [key: string]: any;
164
- updatedComments?: import("../types/comment").Comment[];
165
- updateQA?: import("../types/qa").QA;
166
- }, "nextCommentsApi">;
167
- getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
168
- storyId: string;
169
- useStaging: boolean;
170
- commentsAPIUrl: string;
171
- } & {
172
- lastEventId: string;
65
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CommentsStreamTransformed, "nextCommentsApi">;
66
+ getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CachedData, "nextCommentsApi">;
67
+ getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
68
+ lastEventId?: string;
173
69
  }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
174
- updatedComments: import("../types/comment").Comment[] | undefined;
175
- lastEventId: string | undefined;
70
+ updatedComments: import("../types/comment").Comment[];
71
+ status: import("../types/comments-api").StreamStatus | null;
72
+ lastEventId: string | null;
176
73
  }, "nextCommentsApi">;
177
- getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<{
178
- storyId: string;
179
- useStaging: boolean;
180
- commentsAPIUrl: string;
181
- } & {
182
- token: string;
183
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
184
- expertView: boolean;
185
- }, "nextCommentsApi">;
186
- postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
187
- storyId: string;
188
- useStaging: boolean;
189
- commentsAPIUrl: string;
190
- } & {
191
- token: string;
192
- } & {
193
- question: string;
194
- postAnonymously: boolean;
195
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
74
+ getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").UserRolesTransformed, "nextCommentsApi">;
75
+ postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<import("../types/comments-api").CommentsAPIUrlQuestionParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
196
76
  message: string;
197
77
  }, "nextCommentsApi">;
198
78
  }, never, "nextCommentsApi">;
199
79
  }>, import("@reduxjs/toolkit").AnyAction>, import("@reduxjs/toolkit").Middleware<{}, import("@reduxjs/toolkit/query").RootState<{
200
- getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
201
- storyId: string;
202
- useStaging: boolean;
203
- commentsAPIUrl: string;
204
- } & {
80
+ getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
205
81
  status: import("../types/qa").Status;
206
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, Omit<{
207
- type: string;
208
- children?: import("../types/comment").Comment[];
209
- status?: import("../types/qa").Status;
210
- startDateTime?: string;
211
- endDateTime?: string;
212
- }, "children"> & {
213
- qandas: import("../types/comment").Comment[];
214
- }, "nextCommentsApi">;
215
- getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
216
- storyId: string;
217
- useStaging: boolean;
218
- commentsAPIUrl: string;
219
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
220
- [key: string]: any;
221
- updatedComments?: import("../types/comment").Comment[];
222
- updateQA?: import("../types/qa").QA;
223
- }, "nextCommentsApi">;
224
- getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<{
225
- storyId: string;
226
- useStaging: boolean;
227
- commentsAPIUrl: string;
228
- } & {
229
- lastEventId: string;
82
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CommentsStreamTransformed, "nextCommentsApi">;
83
+ getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").CachedData, "nextCommentsApi">;
84
+ getCatchupUpdates: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlParams & {
85
+ lastEventId?: string;
230
86
  }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
231
- updatedComments: import("../types/comment").Comment[] | undefined;
232
- lastEventId: string | undefined;
87
+ updatedComments: import("../types/comment").Comment[];
88
+ status: import("../types/comments-api").StreamStatus | null;
89
+ lastEventId: string | null;
233
90
  }, "nextCommentsApi">;
234
- getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<{
235
- storyId: string;
236
- useStaging: boolean;
237
- commentsAPIUrl: string;
238
- } & {
239
- token: string;
240
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
241
- expertView: boolean;
242
- }, "nextCommentsApi">;
243
- postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
244
- storyId: string;
245
- useStaging: boolean;
246
- commentsAPIUrl: string;
247
- } & {
248
- token: string;
249
- } & {
250
- question: string;
251
- postAnonymously: boolean;
252
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
91
+ getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<import("../types/comments-api").CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, import("../types/comments-api").UserRolesTransformed, "nextCommentsApi">;
92
+ postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<import("../types/comments-api").CommentsAPIUrlQuestionParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
253
93
  message: string;
254
94
  }, "nextCommentsApi">;
255
95
  }, string, "nextCommentsApi">, ThunkDispatch<any, any, import("@reduxjs/toolkit").AnyAction>>, import("@reduxjs/toolkit").Middleware<{}, any, import("@reduxjs/toolkit").Dispatch<Action<any>>>]>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/qanda-ui",
3
- "version": "0.0.1-beta.33",
3
+ "version": "0.0.1-beta.35",
4
4
  "description": "Components for the Live Q&A (AKA Ask an Expert) UI",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",