@financial-times/qanda-ui 0.0.1-beta.28 → 0.0.1-beta.29

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.
@@ -47,7 +47,7 @@ exports.nextCommentsApi = (0, react_1.createApi)({
47
47
  exports.eventSource.onopen = async () => {
48
48
  console.log(`Connected to SSE: ${sseEndpoint}`);
49
49
  const state = getState();
50
- const lastEventId = state.stream.mostRecentUpdate;
50
+ const lastEventId = state.stream.mostRecentUpdateTime;
51
51
  if (lastEventId !== null && lastEventId !== undefined) {
52
52
  try {
53
53
  await dispatch(exports.nextCommentsApi.endpoints.getCatchupUpdates.initiate({
@@ -74,7 +74,7 @@ exports.nextCommentsApi = (0, react_1.createApi)({
74
74
  });
75
75
  // Store the lastEventId for use in reconnection
76
76
  if (event.lastEventId) {
77
- dispatch((0, stream_1.setMostRecentUpdate)(event.lastEventId));
77
+ dispatch((0, stream_1.setMostRecentUpdateTime)(event.lastEventId));
78
78
  }
79
79
  }
80
80
  }
@@ -15,40 +15,41 @@ const comments_api_1 = require("./comments-api");
15
15
  const stream_1 = require("../store/stream");
16
16
  exports.listenerMiddleware = (0, toolkit_1.createListenerMiddleware)();
17
17
  /**
18
- * Calculates the most recent update timestamp from a list of Q&A questions
18
+ * Calculates the most recent update timestamp and answer id from a Q&A question
19
19
  *
20
- * @param qandas - Array of Comment objects representing questions with nested answers
21
- * @returns The timestamp (in milliseconds) of the most recent answer, 0 if no answers found
20
+ * @param qandas - A Comment object representing a question with nested answers
21
+ * @returns Object containing mostRecentUpdateTime (timestamp) and mostRecentUpdateId (answer id)
22
22
  *
23
23
  * @example
24
- * const timestamp = getMostRecentUpdateTime(qandas);
25
- * // Returns: 167253120 (example timestamp)
24
+ * const { mostRecentUpdateTime, mostRecentUpdateId } = getMostRecentUpdate(qandas);
25
+ * // Returns: { mostRecentUpdateTime: 167253120, mostRecentUpdateId: 'answer-123' }
26
26
  */
27
- // we have a v similar functin in src/utils/qandas.ts
28
- function getMostRecentUpdateTime(qandas) {
29
- if (!qandas.length ||
30
- !Array.isArray(qandas[0].children) ||
31
- !qandas[0].children.length) {
32
- return 0; // no answers or no content at all
27
+ function getMostRecentUpdate(qandaComment) {
28
+ let mostRecentUpdateTime = 0;
29
+ let mostRecentUpdateId = '';
30
+ if (qandaComment.children && Array.isArray(qandaComment.children)) {
31
+ for (const answer of qandaComment?.children) {
32
+ const publishedDateTimestamp = new Date(answer.publishedDate).getTime();
33
+ if (publishedDateTimestamp > mostRecentUpdateTime) {
34
+ mostRecentUpdateTime = publishedDateTimestamp;
35
+ mostRecentUpdateId = answer.id;
36
+ }
37
+ }
33
38
  }
34
- // we should be getting the comments back in a date desc by most recent answer from the API
35
- const mostRecentUpdate = qandas[0].children.reduce((mostRecent, answer) => {
36
- const publishedDateTimestamp = new Date(answer.publishedDate).getTime();
37
- return publishedDateTimestamp > mostRecent
38
- ? publishedDateTimestamp
39
- : mostRecent;
40
- }, 0);
41
- return mostRecentUpdate;
39
+ return { mostRecentUpdateTime, mostRecentUpdateId };
42
40
  }
43
41
  /**
44
- * Listener: Updates most recent timestamp when live Q&A stream data is fetched
42
+ * Listener: Updates most recent timestamp and initiates catchup when live Q&A stream data is fetched
45
43
  *
46
44
  * Responds to successful getQandAStream queries with status='live' and extracts
47
45
  * the most recent answer timestamp to store in Redux state. This timestamp is
48
- * used for reconnection logic and catching up on missed updates.
46
+ * used for reconnection logic and catching up on missed updates. Also automatically
47
+ * initiates a catchup request to get any updates that occurred since the last timestamp.
49
48
  *
50
49
  * @listens nextCommentsApi.endpoints.getQandAStream.matchFulfilled
51
- * @dispatches setMostRecentUpdate - Updates stream.mostRecentUpdate in Redux store
50
+ * @dispatches setMostRecentUpdateTime - Updates stream.mostRecentUpdateTime in Redux store
51
+ * @dispatches setStreamStartMostRecentAnswerId - Updates stream.streamStartMostRecentAnswerId in Redux store
52
+ * @dispatches nextCommentsApi.endpoints.getCatchupUpdates.initiate - Triggers catchup request for missed updates
52
53
  */
53
54
  exports.listenerMiddleware.startListening({
54
55
  matcher: (0, toolkit_1.isAnyOf)(comments_api_1.nextCommentsApi.endpoints.getQandAStream.matchFulfilled),
@@ -57,18 +58,21 @@ exports.listenerMiddleware.startListening({
57
58
  const { storyId, useStaging, commentsAPIUrl, status } = meta.arg.originalArgs;
58
59
  // Check if this is a live stream QUERY, not response. If it is a live query we get questions and answers.
59
60
  const isLiveStream = status === 'live';
60
- if (isLiveStream && payload.qandas) {
61
- const mostRecentUpdate = getMostRecentUpdateTime(payload.qandas);
61
+ if (isLiveStream &&
62
+ Array.isArray(payload.qandas) &&
63
+ payload.qandas.length > 0) {
64
+ const { mostRecentUpdateTime, mostRecentUpdateId } = getMostRecentUpdate(payload.qandas[0]);
62
65
  // Update the store with the most recent update as a string
63
- if (mostRecentUpdate !== undefined) {
64
- console.log(`Setting most recent update for story ${storyId}: ${mostRecentUpdate}`);
65
- listenerApi.dispatch((0, stream_1.setMostRecentUpdate)(`${mostRecentUpdate}`));
66
+ if (mostRecentUpdateTime !== undefined) {
67
+ console.log(`Setting most recent update for story ${storyId}: ${mostRecentUpdateTime}`);
68
+ listenerApi.dispatch((0, stream_1.setMostRecentUpdateTime)(`${mostRecentUpdateTime}`));
69
+ listenerApi.dispatch((0, stream_1.setStreamLoadMostRecentAnswerId)(mostRecentUpdateId));
66
70
  }
67
71
  listenerApi.dispatch(comments_api_1.nextCommentsApi.endpoints.getCatchupUpdates.initiate({
68
72
  storyId: storyId,
69
73
  useStaging: useStaging,
70
74
  commentsAPIUrl: commentsAPIUrl,
71
- lastEventId: `${mostRecentUpdate}`,
75
+ lastEventId: `${mostRecentUpdateTime}`,
72
76
  }));
73
77
  }
74
78
  },
@@ -76,11 +80,13 @@ exports.listenerMiddleware.startListening({
76
80
  /**
77
81
  * Listener: Updates updates cache and most recent update when catchup comments are received
78
82
  *
79
- * Responds to successful getCatchupUpdates queries and adds them to the updates list
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.
80
86
  *
81
87
  * @listens nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled
82
- * @dispatches nextCommentsApi.util.updateQueryData - Updates the cache of updated comments in the store
83
- * @dispatches setMostRecentUpdate - Updates stream.mostRecentUpdate in Redux store
88
+ * @dispatches nextCommentsApi.util.updateQueryData - Updates the cache of updated comments in the store
89
+ * @dispatches setMostRecentUpdateTime - Updates stream.mostRecentUpdateTime with new lastEventId
84
90
  */
85
91
  exports.listenerMiddleware.startListening({
86
92
  matcher: comments_api_1.nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled,
@@ -88,16 +94,28 @@ exports.listenerMiddleware.startListening({
88
94
  const { payload, meta } = action;
89
95
  const { storyId, useStaging, commentsAPIUrl } = meta.arg.originalArgs;
90
96
  const { updatedComments, lastEventId } = payload;
97
+ if (!updatedComments)
98
+ return;
91
99
  console.log('Received catchup updates, new event id:', lastEventId);
92
100
  const updateType = comments_api_1.updateTypes['QA_NEW_REPLY'];
93
- if (Array.isArray(updatedComments) && updatedComments.length) {
101
+ 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;
110
+ }
111
+ return true;
112
+ });
113
+ if (Array.isArray(filteredComments) && filteredComments.length) {
94
114
  listenerApi.dispatch(comments_api_1.nextCommentsApi.util.updateQueryData('getQandAUpdates', { storyId, useStaging, commentsAPIUrl }, (draft) => {
95
115
  draft[updateType] = draft[updateType] || [];
96
- draft[updateType].push(...updatedComments);
116
+ draft[updateType].push(...filteredComments);
97
117
  }));
98
118
  }
99
- if (typeof lastEventId === 'string') {
100
- listenerApi.dispatch((0, stream_1.setMostRecentUpdate)(lastEventId));
101
- }
119
+ listenerApi.dispatch((0, stream_1.setMostRecentUpdateTime)(lastEventId));
102
120
  },
103
121
  });
@@ -1,11 +1,21 @@
1
1
  "use strict";
2
+ /**
3
+ * Redux slice containing stream state and reducers.
4
+ *
5
+ * @remarks
6
+ * This slice provides actions to update:
7
+ * - latestAnsweredQuestionId: he ID for the question with the latest answer, used for scrolling to place
8
+ * - mostRecentUpdateTime: The timestamp or valkey ID of the most recent update
9
+ * - streamLoadMostRecentAnswerId: The ID of the most recent answer when you load the stream data
10
+ */
2
11
  var _a;
3
12
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.setMostRecentUpdate = exports.setLatestAnsweredQuestionId = exports.stream = exports.initialState = void 0;
13
+ exports.setStreamLoadMostRecentAnswerId = exports.setMostRecentUpdateTime = exports.setLatestAnsweredQuestionId = exports.stream = exports.initialState = void 0;
5
14
  const toolkit_1 = require("@reduxjs/toolkit");
6
15
  exports.initialState = {
7
16
  latestAnsweredQuestionId: '',
8
- mostRecentUpdate: null,
17
+ mostRecentUpdateTime: null,
18
+ streamLoadMostRecentAnswerId: null,
9
19
  };
10
20
  exports.stream = (0, toolkit_1.createSlice)({
11
21
  name: 'stream',
@@ -14,9 +24,12 @@ exports.stream = (0, toolkit_1.createSlice)({
14
24
  setLatestAnsweredQuestionId: (state, action) => {
15
25
  state.latestAnsweredQuestionId = action.payload;
16
26
  },
17
- setMostRecentUpdate: (state, action) => {
18
- state.mostRecentUpdate = action.payload;
27
+ setMostRecentUpdateTime: (state, action) => {
28
+ state.mostRecentUpdateTime = action.payload;
29
+ },
30
+ setStreamLoadMostRecentAnswerId: (state, action) => {
31
+ state.streamLoadMostRecentAnswerId = action.payload;
19
32
  },
20
33
  },
21
34
  });
22
- _a = exports.stream.actions, exports.setLatestAnsweredQuestionId = _a.setLatestAnsweredQuestionId, exports.setMostRecentUpdate = _a.setMostRecentUpdate;
35
+ _a = exports.stream.actions, exports.setLatestAnsweredQuestionId = _a.setLatestAnsweredQuestionId, exports.setMostRecentUpdateTime = _a.setMostRecentUpdateTime, exports.setStreamLoadMostRecentAnswerId = _a.setStreamLoadMostRecentAnswerId;
@@ -1,5 +1,5 @@
1
1
  import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
2
- import { setMostRecentUpdate } from '../store/stream';
2
+ import { setMostRecentUpdateTime } from '../store/stream';
3
3
  import { qandaReliability } from './reliability';
4
4
  // NB: this could do with a refactor to split things out into smaller files: see https://redux-toolkit.js.org/rtk-query/usage/code-splitting
5
5
  // NB: adding retry limit would be a good idea https://financialtimes.atlassian.net/browse/CI-2715
@@ -44,7 +44,7 @@ export const nextCommentsApi = createApi({
44
44
  eventSource.onopen = async () => {
45
45
  console.log(`Connected to SSE: ${sseEndpoint}`);
46
46
  const state = getState();
47
- const lastEventId = state.stream.mostRecentUpdate;
47
+ const lastEventId = state.stream.mostRecentUpdateTime;
48
48
  if (lastEventId !== null && lastEventId !== undefined) {
49
49
  try {
50
50
  await dispatch(nextCommentsApi.endpoints.getCatchupUpdates.initiate({
@@ -71,7 +71,7 @@ export const nextCommentsApi = createApi({
71
71
  });
72
72
  // Store the lastEventId for use in reconnection
73
73
  if (event.lastEventId) {
74
- dispatch(setMostRecentUpdate(event.lastEventId));
74
+ dispatch(setMostRecentUpdateTime(event.lastEventId));
75
75
  }
76
76
  }
77
77
  }
@@ -9,43 +9,44 @@
9
9
  */
10
10
  import { createListenerMiddleware, isAnyOf } from '@reduxjs/toolkit';
11
11
  import { nextCommentsApi, updateTypes } from './comments-api';
12
- import { setMostRecentUpdate } from '../store/stream';
12
+ import { setMostRecentUpdateTime, setStreamLoadMostRecentAnswerId, } from '../store/stream';
13
13
  export const listenerMiddleware = createListenerMiddleware();
14
14
  /**
15
- * Calculates the most recent update timestamp from a list of Q&A questions
15
+ * Calculates the most recent update timestamp and answer id from a Q&A question
16
16
  *
17
- * @param qandas - Array of Comment objects representing questions with nested answers
18
- * @returns The timestamp (in milliseconds) of the most recent answer, 0 if no answers found
17
+ * @param qandas - A Comment object representing a question with nested answers
18
+ * @returns Object containing mostRecentUpdateTime (timestamp) and mostRecentUpdateId (answer id)
19
19
  *
20
20
  * @example
21
- * const timestamp = getMostRecentUpdateTime(qandas);
22
- * // Returns: 167253120 (example timestamp)
21
+ * const { mostRecentUpdateTime, mostRecentUpdateId } = getMostRecentUpdate(qandas);
22
+ * // Returns: { mostRecentUpdateTime: 167253120, mostRecentUpdateId: 'answer-123' }
23
23
  */
24
- // we have a v similar functin in src/utils/qandas.ts
25
- function getMostRecentUpdateTime(qandas) {
26
- if (!qandas.length ||
27
- !Array.isArray(qandas[0].children) ||
28
- !qandas[0].children.length) {
29
- return 0; // no answers or no content at all
24
+ function getMostRecentUpdate(qandaComment) {
25
+ let mostRecentUpdateTime = 0;
26
+ let mostRecentUpdateId = '';
27
+ if (qandaComment.children && Array.isArray(qandaComment.children)) {
28
+ for (const answer of qandaComment?.children) {
29
+ const publishedDateTimestamp = new Date(answer.publishedDate).getTime();
30
+ if (publishedDateTimestamp > mostRecentUpdateTime) {
31
+ mostRecentUpdateTime = publishedDateTimestamp;
32
+ mostRecentUpdateId = answer.id;
33
+ }
34
+ }
30
35
  }
31
- // we should be getting the comments back in a date desc by most recent answer from the API
32
- const mostRecentUpdate = qandas[0].children.reduce((mostRecent, answer) => {
33
- const publishedDateTimestamp = new Date(answer.publishedDate).getTime();
34
- return publishedDateTimestamp > mostRecent
35
- ? publishedDateTimestamp
36
- : mostRecent;
37
- }, 0);
38
- return mostRecentUpdate;
36
+ return { mostRecentUpdateTime, mostRecentUpdateId };
39
37
  }
40
38
  /**
41
- * Listener: Updates most recent timestamp when live Q&A stream data is fetched
39
+ * Listener: Updates most recent timestamp and initiates catchup when live Q&A stream data is fetched
42
40
  *
43
41
  * Responds to successful getQandAStream queries with status='live' and extracts
44
42
  * the most recent answer timestamp to store in Redux state. This timestamp is
45
- * used for reconnection logic and catching up on missed updates.
43
+ * used for reconnection logic and catching up on missed updates. Also automatically
44
+ * initiates a catchup request to get any updates that occurred since the last timestamp.
46
45
  *
47
46
  * @listens nextCommentsApi.endpoints.getQandAStream.matchFulfilled
48
- * @dispatches setMostRecentUpdate - Updates stream.mostRecentUpdate in Redux store
47
+ * @dispatches setMostRecentUpdateTime - Updates stream.mostRecentUpdateTime in Redux store
48
+ * @dispatches setStreamStartMostRecentAnswerId - Updates stream.streamStartMostRecentAnswerId in Redux store
49
+ * @dispatches nextCommentsApi.endpoints.getCatchupUpdates.initiate - Triggers catchup request for missed updates
49
50
  */
50
51
  listenerMiddleware.startListening({
51
52
  matcher: isAnyOf(nextCommentsApi.endpoints.getQandAStream.matchFulfilled),
@@ -54,18 +55,21 @@ listenerMiddleware.startListening({
54
55
  const { storyId, useStaging, commentsAPIUrl, status } = meta.arg.originalArgs;
55
56
  // Check if this is a live stream QUERY, not response. If it is a live query we get questions and answers.
56
57
  const isLiveStream = status === 'live';
57
- if (isLiveStream && payload.qandas) {
58
- const mostRecentUpdate = getMostRecentUpdateTime(payload.qandas);
58
+ if (isLiveStream &&
59
+ Array.isArray(payload.qandas) &&
60
+ payload.qandas.length > 0) {
61
+ const { mostRecentUpdateTime, mostRecentUpdateId } = getMostRecentUpdate(payload.qandas[0]);
59
62
  // Update the store with the most recent update as a string
60
- if (mostRecentUpdate !== undefined) {
61
- console.log(`Setting most recent update for story ${storyId}: ${mostRecentUpdate}`);
62
- listenerApi.dispatch(setMostRecentUpdate(`${mostRecentUpdate}`));
63
+ if (mostRecentUpdateTime !== undefined) {
64
+ console.log(`Setting most recent update for story ${storyId}: ${mostRecentUpdateTime}`);
65
+ listenerApi.dispatch(setMostRecentUpdateTime(`${mostRecentUpdateTime}`));
66
+ listenerApi.dispatch(setStreamLoadMostRecentAnswerId(mostRecentUpdateId));
63
67
  }
64
68
  listenerApi.dispatch(nextCommentsApi.endpoints.getCatchupUpdates.initiate({
65
69
  storyId: storyId,
66
70
  useStaging: useStaging,
67
71
  commentsAPIUrl: commentsAPIUrl,
68
- lastEventId: `${mostRecentUpdate}`,
72
+ lastEventId: `${mostRecentUpdateTime}`,
69
73
  }));
70
74
  }
71
75
  },
@@ -73,11 +77,13 @@ listenerMiddleware.startListening({
73
77
  /**
74
78
  * Listener: Updates updates cache and most recent update when catchup comments are received
75
79
  *
76
- * Responds to successful getCatchupUpdates queries and adds them to the updates list
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.
77
83
  *
78
84
  * @listens nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled
79
- * @dispatches nextCommentsApi.util.updateQueryData - Updates the cache of updated comments in the store
80
- * @dispatches setMostRecentUpdate - Updates stream.mostRecentUpdate in Redux store
85
+ * @dispatches nextCommentsApi.util.updateQueryData - Updates the cache of updated comments in the store
86
+ * @dispatches setMostRecentUpdateTime - Updates stream.mostRecentUpdateTime with new lastEventId
81
87
  */
82
88
  listenerMiddleware.startListening({
83
89
  matcher: nextCommentsApi.endpoints.getCatchupUpdates.matchFulfilled,
@@ -85,16 +91,28 @@ listenerMiddleware.startListening({
85
91
  const { payload, meta } = action;
86
92
  const { storyId, useStaging, commentsAPIUrl } = meta.arg.originalArgs;
87
93
  const { updatedComments, lastEventId } = payload;
94
+ if (!updatedComments)
95
+ return;
88
96
  console.log('Received catchup updates, new event id:', lastEventId);
89
97
  const updateType = updateTypes['QA_NEW_REPLY'];
90
- if (Array.isArray(updatedComments) && updatedComments.length) {
98
+ 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;
107
+ }
108
+ return true;
109
+ });
110
+ if (Array.isArray(filteredComments) && filteredComments.length) {
91
111
  listenerApi.dispatch(nextCommentsApi.util.updateQueryData('getQandAUpdates', { storyId, useStaging, commentsAPIUrl }, (draft) => {
92
112
  draft[updateType] = draft[updateType] || [];
93
- draft[updateType].push(...updatedComments);
113
+ draft[updateType].push(...filteredComments);
94
114
  }));
95
115
  }
96
- if (typeof lastEventId === 'string') {
97
- listenerApi.dispatch(setMostRecentUpdate(lastEventId));
98
- }
116
+ listenerApi.dispatch(setMostRecentUpdateTime(lastEventId));
99
117
  },
100
118
  });
@@ -1,7 +1,17 @@
1
+ /**
2
+ * Redux slice containing stream state and reducers.
3
+ *
4
+ * @remarks
5
+ * This slice provides actions to update:
6
+ * - latestAnsweredQuestionId: he ID for the question with the latest answer, used for scrolling to place
7
+ * - mostRecentUpdateTime: The timestamp or valkey ID of the most recent update
8
+ * - streamLoadMostRecentAnswerId: The ID of the most recent answer when you load the stream data
9
+ */
1
10
  import { createSlice } from '@reduxjs/toolkit';
2
11
  export const initialState = {
3
12
  latestAnsweredQuestionId: '',
4
- mostRecentUpdate: null,
13
+ mostRecentUpdateTime: null,
14
+ streamLoadMostRecentAnswerId: null,
5
15
  };
6
16
  export const stream = createSlice({
7
17
  name: 'stream',
@@ -10,9 +20,12 @@ export const stream = createSlice({
10
20
  setLatestAnsweredQuestionId: (state, action) => {
11
21
  state.latestAnsweredQuestionId = action.payload;
12
22
  },
13
- setMostRecentUpdate: (state, action) => {
14
- state.mostRecentUpdate = action.payload;
23
+ setMostRecentUpdateTime: (state, action) => {
24
+ state.mostRecentUpdateTime = action.payload;
25
+ },
26
+ setStreamLoadMostRecentAnswerId: (state, action) => {
27
+ state.streamLoadMostRecentAnswerId = action.payload;
15
28
  },
16
29
  },
17
30
  });
18
- export const { setLatestAnsweredQuestionId, setMostRecentUpdate } = stream.actions;
31
+ export const { setLatestAnsweredQuestionId, setMostRecentUpdateTime, setStreamLoadMostRecentAnswerId, } = stream.actions;
@@ -1,8 +1,18 @@
1
+ /**
2
+ * Redux slice containing stream state and reducers.
3
+ *
4
+ * @remarks
5
+ * This slice provides actions to update:
6
+ * - latestAnsweredQuestionId: he ID for the question with the latest answer, used for scrolling to place
7
+ * - mostRecentUpdateTime: The timestamp or valkey ID of the most recent update
8
+ * - streamLoadMostRecentAnswerId: The ID of the most recent answer when you load the stream data
9
+ */
1
10
  import { Slice } from '@reduxjs/toolkit';
2
11
  export type StreamState = {
3
12
  latestAnsweredQuestionId: string;
4
- mostRecentUpdate: string | null;
13
+ mostRecentUpdateTime: string | null;
14
+ streamLoadMostRecentAnswerId: string | null;
5
15
  };
6
16
  export declare const initialState: StreamState;
7
17
  export declare const stream: Slice;
8
- export declare const setLatestAnsweredQuestionId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, `${string}/${string}`> | import("@reduxjs/toolkit").ActionCreatorWithoutPayload<`${string}/${string}`>, setMostRecentUpdate: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, `${string}/${string}`> | import("@reduxjs/toolkit").ActionCreatorWithoutPayload<`${string}/${string}`>;
18
+ export declare const setLatestAnsweredQuestionId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, `${string}/${string}`> | import("@reduxjs/toolkit").ActionCreatorWithoutPayload<`${string}/${string}`>, setMostRecentUpdateTime: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, `${string}/${string}`> | import("@reduxjs/toolkit").ActionCreatorWithoutPayload<`${string}/${string}`>, setStreamLoadMostRecentAnswerId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, `${string}/${string}`> | import("@reduxjs/toolkit").ActionCreatorWithoutPayload<`${string}/${string}`>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/qanda-ui",
3
- "version": "0.0.1-beta.28",
3
+ "version": "0.0.1-beta.29",
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",