@financial-times/qanda-ui 0.0.1-beta.7 → 0.0.1-beta.8

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 (37) hide show
  1. package/dist/cjs/components/EmbedFormField/index.js +4 -0
  2. package/dist/cjs/components/ExpertDrawer/index.js +36 -0
  3. package/dist/cjs/components/FloatingActionBar/index.js +20 -5
  4. package/dist/cjs/components/Loader/index.js +7 -0
  5. package/dist/cjs/components/Overlay/index.js +12 -2
  6. package/dist/cjs/components/QandaContainer.js +17 -2
  7. package/dist/cjs/components/QuestionForm/index.js +2 -1
  8. package/dist/cjs/components/Stream/index.js +31 -0
  9. package/dist/cjs/services/comments-api.js +6 -0
  10. package/dist/cjs/store/expertSlice.js +26 -0
  11. package/dist/cjs/store/index.js +2 -0
  12. package/dist/esm/components/EmbedFormField/index.js +5 -1
  13. package/dist/esm/components/ExpertDrawer/index.js +31 -0
  14. package/dist/esm/components/FloatingActionBar/index.js +21 -6
  15. package/dist/esm/components/Loader/index.js +4 -0
  16. package/dist/esm/components/Overlay/index.js +9 -2
  17. package/dist/esm/components/QandaContainer.js +19 -4
  18. package/dist/esm/components/QuestionForm/index.js +2 -1
  19. package/dist/esm/components/Stream/index.js +26 -0
  20. package/dist/esm/services/comments-api.js +6 -0
  21. package/dist/esm/store/expertSlice.js +22 -0
  22. package/dist/esm/store/index.js +2 -0
  23. package/dist/qanda.scss +57 -21
  24. package/dist/types/components/EmbedFormField/index.d.ts +1 -1
  25. package/dist/types/components/ExpertDrawer/index.d.ts +2 -0
  26. package/dist/types/components/Loader/index.d.ts +14 -0
  27. package/dist/types/components/Overlay/index.d.ts +6 -1
  28. package/dist/types/services/comments-api.d.ts +23 -20
  29. package/dist/types/store/expertSlice.d.ts +15 -0
  30. package/dist/types/store/index.d.ts +18 -4
  31. package/package.json +1 -1
  32. package/dist/cjs/components/QandaOComments.js +0 -43
  33. package/dist/cjs/components/Stream.js +0 -28
  34. package/dist/esm/components/QandaOComments.js +0 -38
  35. package/dist/esm/components/Stream.js +0 -23
  36. package/dist/types/components/QandaOComments.d.ts +0 -2
  37. /package/dist/types/components/{Stream.d.ts → Stream/index.d.ts} +0 -0
@@ -8,6 +8,10 @@ const react_dom_1 = require("react-dom");
8
8
  function EmbedFormField({ container }) {
9
9
  const dispatch = (0, react_redux_1.useDispatch)();
10
10
  const [portalTarget, setPortalTarget] = (0, react_1.useState)();
11
+ const expertView = (0, react_redux_1.useSelector)((state) => state.expert.expertView);
12
+ if (expertView) {
13
+ return null;
14
+ }
11
15
  (0, react_1.useEffect)(() => {
12
16
  if (container) {
13
17
  const portalContainer = document.createElement('div');
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("preact/jsx-runtime");
7
+ const o_comments_1 = __importDefault(require("@financial-times/o-comments"));
8
+ const react_1 = require("react");
9
+ const react_redux_1 = require("react-redux");
10
+ const expertSlice_1 = require("../../store/expertSlice");
11
+ const QandaProvider_1 = require("../QandaProvider");
12
+ const reliability_1 = require("../../services/reliability");
13
+ const Overlay_1 = __importDefault(require("../Overlay"));
14
+ function ExpertDrawer() {
15
+ const dispatch = (0, react_redux_1.useDispatch)();
16
+ const { storyId, useStaging, title } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
17
+ const { isExpertFormOpen, expertView } = (0, react_redux_1.useSelector)((state) => state.expert);
18
+ (0, react_1.useEffect)(() => {
19
+ if (isExpertFormOpen) {
20
+ try {
21
+ o_comments_1.default.init();
22
+ }
23
+ catch (error) {
24
+ reliability_1.qandaReliability.failure('expert-load-coral-ui', error);
25
+ }
26
+ }
27
+ }, [isExpertFormOpen]);
28
+ if (!isExpertFormOpen) {
29
+ return null;
30
+ }
31
+ return ((0, jsx_runtime_1.jsx)(Overlay_1.default, { title: "Live Q&A", closeCallback: () => dispatch((0, expertSlice_1.closeExpertForm)()), options: {
32
+ shadow: true,
33
+ noBackground: true,
34
+ }, children: (0, jsx_runtime_1.jsx)("div", { className: "o-comments", id: "o-comments-stream", "data-o-component": "o-comments", "data-o-comments-article-id": storyId, "data-o-comments-article-url": `https://www.ft.com/content/${storyId}`, "data-o-comments-use-staging-environment": useStaging, "data-o-comments-title": title }) }));
35
+ }
36
+ exports.default = ExpertDrawer;
@@ -4,6 +4,7 @@ const jsx_runtime_1 = require("preact/jsx-runtime");
4
4
  const react_redux_1 = require("react-redux");
5
5
  const o3_button_1 = require("@financial-times/o3-button");
6
6
  const questionSlice_1 = require("../../store/questionSlice");
7
+ const expertSlice_1 = require("../../store/expertSlice");
7
8
  const useIsMobile_1 = require("../../hooks/useIsMobile");
8
9
  const comments_api_1 = require("../../services/comments-api");
9
10
  const updateComments_1 = require("../../store/updateComments");
@@ -26,12 +27,26 @@ function FloatingActionBar() {
26
27
  element.scrollIntoView({ behavior: 'smooth', block: 'start' });
27
28
  }
28
29
  };
30
+ const showExpertView = (0, react_redux_1.useSelector)((state) => state.expert.expertView);
31
+ const userConfig = {
32
+ descripton: 'Ask a question',
33
+ testIdSuffix: 'ask-a-question-button',
34
+ onClick: () => dispatch((0, questionSlice_1.openForm)()),
35
+ dataTrackable: 'open_question_form',
36
+ };
37
+ const expertConfig = {
38
+ descripton: 'Answer Q&A',
39
+ testIdSuffix: 'answer-a-qanda-button',
40
+ onClick: () => dispatch((0, expertSlice_1.openExpertForm)()),
41
+ dataTrackable: 'open_expert_drawer',
42
+ };
43
+ const view = showExpertView ? expertConfig : userConfig;
29
44
  return ((0, jsx_runtime_1.jsxs)("div", { className: "floating-action-bar__container", "data-trackable": "floating-action-bar", children: [(0, jsx_runtime_1.jsx)("div", { className: "floating-action-bar__fade-overlay" }), (0, jsx_runtime_1.jsxs)("div", { className: "floating-action-bar", "data-testid": "floating-action-bar", children: [(0, jsx_runtime_1.jsx)("div", { className: "o-labels-indicator--live o3-type-label floating-action-bar__live-container", children: (0, jsx_runtime_1.jsx)("span", { className: "o-labels-indicator__status floating-action-bar__live-label", children: "Live" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "floating-action-bar__buttons", children: [(0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: {
30
- ariaLabel: 'Ask a question',
31
- 'data-trackable': 'open_question_form',
32
- 'data-trackable-context-action': 'Ask a question',
33
- 'data-testid': 'floating-action-bar__ask-a-question-button',
34
- }, label: "Ask a question", type: "ghost", theme: "mono", icon: "edit", iconOnly: isMobile, onClick: () => dispatch((0, questionSlice_1.openForm)()) }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: {
45
+ ariaLabel: view.descripton,
46
+ 'data-trackable': view.dataTrackable,
47
+ 'data-trackable-context-action': view.descripton,
48
+ 'data-testid': `floating-action-bar__${view.testIdSuffix}`,
49
+ }, label: view.descripton, type: "ghost", theme: "mono", icon: "edit", iconOnly: isMobile, onClick: view.onClick }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: {
35
50
  ariaLabel: 'Back to top',
36
51
  'data-trackable': 'back_to_top',
37
52
  'data-trackable-context-action': 'Back to top',
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = Loader;
4
+ const jsx_runtime_1 = require("preact/jsx-runtime");
5
+ function Loader({ size = 'small', theme = 'light', extraClassNames = '', }) {
6
+ return ((0, jsx_runtime_1.jsx)("div", { className: `o-loading o-loading--${theme} o-loading--${size} ${extraClassNames}`, role: "progressbar", "aria-label": "Loading content" }));
7
+ }
@@ -1,8 +1,18 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const jsx_runtime_1 = require("preact/jsx-runtime");
4
7
  const o3_button_1 = require("@financial-times/o3-button");
5
- function Overlay({ title, children, closeCallback }) {
6
- return ((0, jsx_runtime_1.jsxs)("div", { className: "overlay", children: [(0, jsx_runtime_1.jsx)("div", { className: "overlay__outer" }), (0, jsx_runtime_1.jsxs)("div", { className: "overlay__inner", children: [(0, jsx_runtime_1.jsx)("div", { className: "overlay__header", children: (0, jsx_runtime_1.jsxs)("div", { className: "overlay__header-bar", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-type-body-highlight overlay__header-title", children: title }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: { className: 'overlay__header-close' }, iconOnly: true, icon: "cross", label: "Close", type: "ghost", theme: "mono", onClick: () => closeCallback() })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "overlay__content", children: children })] })] }));
8
+ const classnames_1 = __importDefault(require("classnames"));
9
+ function Overlay({ title, children, closeCallback, options = {}, }) {
10
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "overlay", children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)([
11
+ 'overlay__outer',
12
+ { 'overlay__outer--no-background': options.noBackground },
13
+ ]) }), (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)([
14
+ 'overlay__inner',
15
+ { 'overlay__inner--shadow': options.shadow },
16
+ ]), children: [(0, jsx_runtime_1.jsx)("div", { className: "overlay__header", children: (0, jsx_runtime_1.jsxs)("div", { className: "overlay__header-bar", children: [(0, jsx_runtime_1.jsx)("span", { className: "o3-type-body-highlight overlay__header-title", children: title }), (0, jsx_runtime_1.jsx)(o3_button_1.Button, { attributes: { className: 'overlay__header-close' }, iconOnly: true, icon: "cross", label: "Close", type: "ghost", theme: "mono", onClick: () => closeCallback() })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "overlay__content", children: children })] })] }));
7
17
  }
8
18
  exports.default = Overlay;
@@ -7,9 +7,10 @@ const jsx_runtime_1 = require("preact/jsx-runtime");
7
7
  const react_1 = require("react");
8
8
  const react_redux_1 = require("react-redux");
9
9
  const user_1 = require("../store/user");
10
+ const expertSlice_1 = require("../store/expertSlice");
10
11
  const QuestionForm_1 = __importDefault(require("./QuestionForm"));
11
12
  const UserInfo_1 = __importDefault(require("./UserInfo"));
12
- const QandaOComments_1 = __importDefault(require("./QandaOComments"));
13
+ const ExpertDrawer_1 = __importDefault(require("./ExpertDrawer"));
13
14
  const Stream_1 = __importDefault(require("./Stream"));
14
15
  const Calendar_1 = __importDefault(require("./Calendar"));
15
16
  const CountdownTimer_1 = __importDefault(require("./CountdownTimer"));
@@ -31,6 +32,20 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
31
32
  commentsAPIUrl,
32
33
  });
33
34
  const status = updatedQA?.status || data?.status || 'pre-live';
35
+ const profile = (0, react_redux_1.useSelector)((state) => state.user.profile);
36
+ const { data: roleData } = (0, comments_api_1.useGetUserRolesQuery)({
37
+ storyId,
38
+ useStaging,
39
+ commentsAPIUrl,
40
+ token: profile.token,
41
+ }, { skip: !profile.token });
42
+ const expertView = roleData?.expertView;
43
+ (0, react_1.useEffect)(() => {
44
+ if (expertView !== undefined) {
45
+ dispatch((0, expertSlice_1.setExpertView)(expertView));
46
+ }
47
+ }, [expertView, dispatch]);
48
+ const showExpertView = (0, react_redux_1.useSelector)((state) => state.expert.expertView);
34
49
  (0, react_1.useEffect)(() => {
35
50
  dispatch((0, user_1.getUserProfile)({ useStaging, commentsAPIUrl }));
36
51
  return () => {
@@ -38,6 +53,6 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
38
53
  dispatch(comments_api_1.nextCommentsApi.util.resetApiState());
39
54
  };
40
55
  }, [dispatch, storyId]);
41
- return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-container", "data-trackable": "liveqa", children: [error && ((0, jsx_runtime_1.jsxs)("p", { children: ["We are not able to load the Q&A at the moment. Please try again later. Error:", error] })), !isLoading && !error && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [status !== 'close' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(QuestionForm_1.default, {}), (0, jsx_runtime_1.jsx)(CountdownTimer_1.default, { container: countdownTimerContainer })] })), status === 'pre-live' && (0, jsx_runtime_1.jsx)(Calendar_1.default, {}), (0, jsx_runtime_1.jsx)(Stream_1.default, {}), status !== 'close' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(UserInfo_1.default, {}), (0, jsx_runtime_1.jsx)(QandaOComments_1.default, {})] })), data?.status === 'live' && (0, jsx_runtime_1.jsx)(FloatingActionBar_1.default, {}), (0, jsx_runtime_1.jsx)(EmbedFormField_1.default, { container: embedFormFieldContainer })] }))] }));
56
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-container", "data-trackable": "liveqa", children: [error && ((0, jsx_runtime_1.jsxs)("p", { children: ["We are not able to load the Q&A at the moment. Please try again later. Error:", error] })), !isLoading && !error && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [status !== 'close' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showExpertView ? (0, jsx_runtime_1.jsx)(ExpertDrawer_1.default, {}) : (0, jsx_runtime_1.jsx)(QuestionForm_1.default, {}), (0, jsx_runtime_1.jsx)(CountdownTimer_1.default, { container: countdownTimerContainer })] })), status === 'pre-live' && (0, jsx_runtime_1.jsx)(Calendar_1.default, {}), (0, jsx_runtime_1.jsx)(Stream_1.default, {}), status !== 'close' && ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(UserInfo_1.default, {}) })), data?.status === 'live' && (0, jsx_runtime_1.jsx)(FloatingActionBar_1.default, {}), (0, jsx_runtime_1.jsx)(EmbedFormField_1.default, { container: embedFormFieldContainer })] }))] }));
42
57
  }
43
58
  exports.default = QandaContainer;
@@ -12,6 +12,7 @@ const questionSlice_1 = require("../../store/questionSlice");
12
12
  const QandaProvider_1 = require("../QandaProvider");
13
13
  const comments_api_1 = require("../../services/comments-api");
14
14
  const Overlay_1 = __importDefault(require("../Overlay"));
15
+ const Loader_1 = __importDefault(require("../Loader"));
15
16
  const o3_button_1 = require("@financial-times/o3-button");
16
17
  const MessageBox_1 = __importDefault(require("../Overlay/MessageBox"));
17
18
  const user_1 = require("../../store/user");
@@ -151,5 +152,5 @@ function QuestionForm() {
151
152
  },
152
153
  // @ts-expect-error - o3 Button is currently accepting a JSX element as label
153
154
  // but the type definition needs to be updated by Origami team
154
- label: isLoading ? ((0, jsx_runtime_1.jsx)("div", { class: "o-loading o-loading--light o-loading--small" })) : ('Submit your question'), type: "primary", theme: "mono" }), !profile?.token && ((0, jsx_runtime_1.jsx)("span", { className: "o3-type-detail", children: "Submitting a question will create a community profile with FT." }))] }) })), isSuccess && ((0, jsx_runtime_1.jsx)(MessageBox_1.default, { color: "#00572C", styles: "o-message--alert o-message--success", title: "Question sent", text: "Your question is in the queue and will be reviewed by our editors." })), error && ((0, jsx_runtime_1.jsx)(MessageBox_1.default, { color: "var(--o3-color-use-case-error)", styles: "o-message--alert o-message--error", title: "Connection issue", text: "Could not submit question: [error messages to come in another ticket]" })), (0, jsx_runtime_1.jsx)(MessageBox_1.default, { styles: "o-message--notice o-message--inform", title: (0, messagesByUserType_1.getTitleByUser)(profile), text: (0, messagesByUserType_1.getTextByUser)(profile) })] }));
155
+ label: isLoading ? (0, jsx_runtime_1.jsx)(Loader_1.default, {}) : 'Submit your question', type: "primary", theme: "mono" }), !profile?.token && ((0, jsx_runtime_1.jsx)("span", { className: "o3-type-detail", children: "Submitting a question will create a community profile with FT." }))] }) })), isSuccess && ((0, jsx_runtime_1.jsx)(MessageBox_1.default, { color: "#00572C", styles: "o-message--alert o-message--success", title: "Question sent", text: "Your question is in the queue and will be reviewed by our editors." })), error && ((0, jsx_runtime_1.jsx)(MessageBox_1.default, { color: "var(--o3-color-use-case-error)", styles: "o-message--alert o-message--error", title: "Connection issue", text: "Could not submit question: [error messages to come in another ticket]" })), (0, jsx_runtime_1.jsx)(MessageBox_1.default, { styles: "o-message--notice o-message--inform", title: (0, messagesByUserType_1.getTitleByUser)(profile), text: (0, messagesByUserType_1.getTextByUser)(profile) })] }));
155
156
  }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const jsx_runtime_1 = require("preact/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const react_redux_1 = require("react-redux");
9
+ const index_1 = __importDefault(require("../QandaBlock/index"));
10
+ const QandaProvider_1 = require("../QandaProvider");
11
+ const Loader_1 = __importDefault(require("../Loader"));
12
+ const comments_api_1 = require("../../services/comments-api");
13
+ function Stream() {
14
+ const latestAnsweredQuestionId = (0, react_redux_1.useSelector)((state) => state.stream.latestAnsweredQuestionId);
15
+ const { storyId, useStaging, commentsAPIUrl } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
16
+ const { data, error, isLoading } = (0, comments_api_1.useGetQandAStreamQuery)({
17
+ storyId,
18
+ useStaging,
19
+ commentsAPIUrl,
20
+ });
21
+ (0, react_1.useEffect)(() => {
22
+ const element = document.getElementById(latestAnsweredQuestionId);
23
+ element?.scrollIntoView({ behavior: 'smooth' });
24
+ }, [latestAnsweredQuestionId]);
25
+ return ((0, jsx_runtime_1.jsxs)("section", { className: "qanda__stream", children: [(0, jsx_runtime_1.jsx)("h2", { className: "o3-type-title-md qanda__stream-title", children: data && data.status && data.status === 'pre-live'
26
+ ? 'Top questions so far'
27
+ : 'What our readers are asking' }), (0, jsx_runtime_1.jsxs)("div", { className: "qanda__blocks-container", "data-testid": "qanda__blocks-container", id: "qanda__blocks-container", "aria-busy": isLoading, children: [isLoading && ((0, jsx_runtime_1.jsx)(Loader_1.default, { theme: "dark", extraClassNames: "qanda__stream-loader" })), error && (0, jsx_runtime_1.jsx)("p", { children: "Failed to load Q&A stream" }), data &&
28
+ data.qandas &&
29
+ data.qandas.map((qandaBlock) => ((0, jsx_runtime_1.jsx)(index_1.default, { id: qandaBlock.id, body: qandaBlock.body, type: "comment", publishedDate: qandaBlock.publishedDate, author: qandaBlock.author, byline: qandaBlock.byline, showAnswerTime: data.status === 'live', children: qandaBlock.children }, qandaBlock.id)))] })] }));
30
+ }
31
+ exports.default = Stream;
@@ -101,6 +101,12 @@ exports.nextCommentsApi = (0, react_1.createApi)({
101
101
  Authorization: `Bearer ${token}`,
102
102
  },
103
103
  }),
104
+ transformResponse: (response, meta, arg) => {
105
+ const allowedRoles = ['ADMIN', 'MODERATOR'];
106
+ const { isExpert, isStaffExpert, role } = response;
107
+ const expertView = isExpert || isStaffExpert || !!(role && allowedRoles.includes(role));
108
+ return { expertView };
109
+ },
104
110
  }),
105
111
  // POST A QUESTION
106
112
  postQuestion: builder.mutation({
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.setExpertView = exports.closeExpertForm = exports.openExpertForm = exports.expertSlice = void 0;
5
+ const toolkit_1 = require("@reduxjs/toolkit");
6
+ const initialState = {
7
+ isExpertFormOpen: false,
8
+ expertView: undefined,
9
+ };
10
+ exports.expertSlice = (0, toolkit_1.createSlice)({
11
+ name: 'expert',
12
+ initialState,
13
+ reducers: {
14
+ openExpertForm: (state) => {
15
+ state.isExpertFormOpen = true;
16
+ },
17
+ closeExpertForm: (state) => {
18
+ state.isExpertFormOpen = false;
19
+ },
20
+ setExpertView: (state, action) => {
21
+ state.expertView = action.payload;
22
+ },
23
+ },
24
+ });
25
+ _a = exports.expertSlice.actions, exports.openExpertForm = _a.openExpertForm, exports.closeExpertForm = _a.closeExpertForm, exports.setExpertView = _a.setExpertView;
26
+ exports.default = exports.expertSlice.reducer;
@@ -9,12 +9,14 @@ const query_1 = require("@reduxjs/toolkit/query");
9
9
  const questionSlice_1 = __importDefault(require("./questionSlice"));
10
10
  const stream_1 = require("./stream");
11
11
  const user_1 = require("./user");
12
+ const expertSlice_1 = __importDefault(require("./expertSlice"));
12
13
  const comments_api_1 = require("../services/comments-api");
13
14
  const error_middleware_1 = require("../services/error-middleware");
14
15
  exports.rootReducer = (0, toolkit_1.combineReducers)({
15
16
  stream: stream_1.stream.reducer,
16
17
  question: questionSlice_1.default,
17
18
  user: user_1.user.reducer,
19
+ expert: expertSlice_1.default,
18
20
  nextCommentsApi: comments_api_1.nextCommentsApi.reducer,
19
21
  });
20
22
  exports.store = (0, toolkit_1.configureStore)({
@@ -1,11 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
- import { useDispatch } from 'react-redux';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
3
  import { openForm } from '../../store/questionSlice';
4
4
  import { useEffect, useState } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  function EmbedFormField({ container }) {
7
7
  const dispatch = useDispatch();
8
8
  const [portalTarget, setPortalTarget] = useState();
9
+ const expertView = useSelector((state) => state.expert.expertView);
10
+ if (expertView) {
11
+ return null;
12
+ }
9
13
  useEffect(() => {
10
14
  if (container) {
11
15
  const portalContainer = document.createElement('div');
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import OComments from '@financial-times/o-comments';
3
+ import { useEffect, useContext } from 'react';
4
+ import { useDispatch, useSelector } from 'react-redux';
5
+ import { closeExpertForm } from '../../store/expertSlice';
6
+ import { QandaContext } from '../QandaProvider';
7
+ import { qandaReliability } from '../../services/reliability';
8
+ import Overlay from '../Overlay';
9
+ function ExpertDrawer() {
10
+ const dispatch = useDispatch();
11
+ const { storyId, useStaging, title } = useContext(QandaContext);
12
+ const { isExpertFormOpen, expertView } = useSelector((state) => state.expert);
13
+ useEffect(() => {
14
+ if (isExpertFormOpen) {
15
+ try {
16
+ OComments.init();
17
+ }
18
+ catch (error) {
19
+ qandaReliability.failure('expert-load-coral-ui', error);
20
+ }
21
+ }
22
+ }, [isExpertFormOpen]);
23
+ if (!isExpertFormOpen) {
24
+ return null;
25
+ }
26
+ return (_jsx(Overlay, { title: "Live Q&A", closeCallback: () => dispatch(closeExpertForm()), options: {
27
+ shadow: true,
28
+ noBackground: true,
29
+ }, children: _jsx("div", { className: "o-comments", id: "o-comments-stream", "data-o-component": "o-comments", "data-o-comments-article-id": storyId, "data-o-comments-article-url": `https://www.ft.com/content/${storyId}`, "data-o-comments-use-staging-environment": useStaging, "data-o-comments-title": title }) }));
30
+ }
31
+ export default ExpertDrawer;
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { useDispatch } from 'react-redux';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
3
  import { Button } from '@financial-times/o3-button';
4
4
  import { openForm } from '../../store/questionSlice';
5
+ import { openExpertForm } from '../../store/expertSlice';
5
6
  import { useIsMobile } from '../../hooks/useIsMobile';
6
7
  import { useUpdatedComments } from '../../services/comments-api';
7
8
  import { updateComments } from '../../store/updateComments';
@@ -24,12 +25,26 @@ function FloatingActionBar() {
24
25
  element.scrollIntoView({ behavior: 'smooth', block: 'start' });
25
26
  }
26
27
  };
28
+ const showExpertView = useSelector((state) => state.expert.expertView);
29
+ const userConfig = {
30
+ descripton: 'Ask a question',
31
+ testIdSuffix: 'ask-a-question-button',
32
+ onClick: () => dispatch(openForm()),
33
+ dataTrackable: 'open_question_form',
34
+ };
35
+ const expertConfig = {
36
+ descripton: 'Answer Q&A',
37
+ testIdSuffix: 'answer-a-qanda-button',
38
+ onClick: () => dispatch(openExpertForm()),
39
+ dataTrackable: 'open_expert_drawer',
40
+ };
41
+ const view = showExpertView ? expertConfig : userConfig;
27
42
  return (_jsxs("div", { className: "floating-action-bar__container", "data-trackable": "floating-action-bar", children: [_jsx("div", { className: "floating-action-bar__fade-overlay" }), _jsxs("div", { className: "floating-action-bar", "data-testid": "floating-action-bar", children: [_jsx("div", { className: "o-labels-indicator--live o3-type-label floating-action-bar__live-container", children: _jsx("span", { className: "o-labels-indicator__status floating-action-bar__live-label", children: "Live" }) }), _jsxs("div", { className: "floating-action-bar__buttons", children: [_jsx(Button, { attributes: {
28
- ariaLabel: 'Ask a question',
29
- 'data-trackable': 'open_question_form',
30
- 'data-trackable-context-action': 'Ask a question',
31
- 'data-testid': 'floating-action-bar__ask-a-question-button',
32
- }, label: "Ask a question", type: "ghost", theme: "mono", icon: "edit", iconOnly: isMobile, onClick: () => dispatch(openForm()) }), _jsx(Button, { attributes: {
43
+ ariaLabel: view.descripton,
44
+ 'data-trackable': view.dataTrackable,
45
+ 'data-trackable-context-action': view.descripton,
46
+ 'data-testid': `floating-action-bar__${view.testIdSuffix}`,
47
+ }, label: view.descripton, type: "ghost", theme: "mono", icon: "edit", iconOnly: isMobile, onClick: view.onClick }), _jsx(Button, { attributes: {
33
48
  ariaLabel: 'Back to top',
34
49
  'data-trackable': 'back_to_top',
35
50
  'data-trackable-context-action': 'Back to top',
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ export default function Loader({ size = 'small', theme = 'light', extraClassNames = '', }) {
3
+ return (_jsx("div", { className: `o-loading o-loading--${theme} o-loading--${size} ${extraClassNames}`, role: "progressbar", "aria-label": "Loading content" }));
4
+ }
@@ -1,6 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { Button } from '@financial-times/o3-button';
3
- function Overlay({ title, children, closeCallback }) {
4
- return (_jsxs("div", { className: "overlay", children: [_jsx("div", { className: "overlay__outer" }), _jsxs("div", { className: "overlay__inner", children: [_jsx("div", { className: "overlay__header", children: _jsxs("div", { className: "overlay__header-bar", children: [_jsx("span", { className: "o3-type-body-highlight overlay__header-title", children: title }), _jsx(Button, { attributes: { className: 'overlay__header-close' }, iconOnly: true, icon: "cross", label: "Close", type: "ghost", theme: "mono", onClick: () => closeCallback() })] }) }), _jsx("div", { className: "overlay__content", children: children })] })] }));
3
+ import classNames from 'classnames';
4
+ function Overlay({ title, children, closeCallback, options = {}, }) {
5
+ return (_jsxs("div", { className: "overlay", children: [_jsx("div", { className: classNames([
6
+ 'overlay__outer',
7
+ { 'overlay__outer--no-background': options.noBackground },
8
+ ]) }), _jsxs("div", { className: classNames([
9
+ 'overlay__inner',
10
+ { 'overlay__inner--shadow': options.shadow },
11
+ ]), children: [_jsx("div", { className: "overlay__header", children: _jsxs("div", { className: "overlay__header-bar", children: [_jsx("span", { className: "o3-type-body-highlight overlay__header-title", children: title }), _jsx(Button, { attributes: { className: 'overlay__header-close' }, iconOnly: true, icon: "cross", label: "Close", type: "ghost", theme: "mono", onClick: () => closeCallback() })] }) }), _jsx("div", { className: "overlay__content", children: children })] })] }));
5
12
  }
6
13
  export default Overlay;
@@ -1,17 +1,18 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "preact/jsx-runtime";
2
2
  import { useEffect, useContext } from 'react';
3
- import { useDispatch } from 'react-redux';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
4
  import { getUserProfile } from '../store/user';
5
+ import { setExpertView } from '../store/expertSlice';
5
6
  import QuestionForm from './QuestionForm';
6
7
  import UserInfo from './UserInfo';
7
- import QandaOComments from './QandaOComments';
8
+ import ExpertDrawer from './ExpertDrawer';
8
9
  import Stream from './Stream';
9
10
  import Calendar from './Calendar';
10
11
  import CountdownTimer from './CountdownTimer';
11
12
  import FloatingActionBar from './FloatingActionBar';
12
13
  import EmbedFormField from './EmbedFormField';
13
14
  import { QandaContext } from './QandaProvider';
14
- import { useGetQandAStreamQuery, useUpdatedQA, nextCommentsApi, } from '../services/comments-api';
15
+ import { useGetQandAStreamQuery, useUpdatedQA, useGetUserRolesQuery, nextCommentsApi, } from '../services/comments-api';
15
16
  function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
16
17
  const { storyId, useStaging, commentsAPIUrl } = useContext(QandaContext);
17
18
  const dispatch = useDispatch();
@@ -26,6 +27,20 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
26
27
  commentsAPIUrl,
27
28
  });
28
29
  const status = updatedQA?.status || data?.status || 'pre-live';
30
+ const profile = useSelector((state) => state.user.profile);
31
+ const { data: roleData } = useGetUserRolesQuery({
32
+ storyId,
33
+ useStaging,
34
+ commentsAPIUrl,
35
+ token: profile.token,
36
+ }, { skip: !profile.token });
37
+ const expertView = roleData?.expertView;
38
+ useEffect(() => {
39
+ if (expertView !== undefined) {
40
+ dispatch(setExpertView(expertView));
41
+ }
42
+ }, [expertView, dispatch]);
43
+ const showExpertView = useSelector((state) => state.expert.expertView);
29
44
  useEffect(() => {
30
45
  dispatch(getUserProfile({ useStaging, commentsAPIUrl }));
31
46
  return () => {
@@ -33,6 +48,6 @@ function QandaContainer({ embedFormFieldContainer, countdownTimerContainer, }) {
33
48
  dispatch(nextCommentsApi.util.resetApiState());
34
49
  };
35
50
  }, [dispatch, storyId]);
36
- return (_jsxs("div", { className: "qanda-container", "data-trackable": "liveqa", children: [error && (_jsxs("p", { children: ["We are not able to load the Q&A at the moment. Please try again later. Error:", error] })), !isLoading && !error && (_jsxs(_Fragment, { children: [status !== 'close' && (_jsxs(_Fragment, { children: [_jsx(QuestionForm, {}), _jsx(CountdownTimer, { container: countdownTimerContainer })] })), status === 'pre-live' && _jsx(Calendar, {}), _jsx(Stream, {}), status !== 'close' && (_jsxs(_Fragment, { children: [_jsx(UserInfo, {}), _jsx(QandaOComments, {})] })), data?.status === 'live' && _jsx(FloatingActionBar, {}), _jsx(EmbedFormField, { container: embedFormFieldContainer })] }))] }));
51
+ return (_jsxs("div", { className: "qanda-container", "data-trackable": "liveqa", children: [error && (_jsxs("p", { children: ["We are not able to load the Q&A at the moment. Please try again later. Error:", error] })), !isLoading && !error && (_jsxs(_Fragment, { children: [status !== 'close' && (_jsxs(_Fragment, { children: [showExpertView ? _jsx(ExpertDrawer, {}) : _jsx(QuestionForm, {}), _jsx(CountdownTimer, { container: countdownTimerContainer })] })), status === 'pre-live' && _jsx(Calendar, {}), _jsx(Stream, {}), status !== 'close' && (_jsx(_Fragment, { children: _jsx(UserInfo, {}) })), data?.status === 'live' && _jsx(FloatingActionBar, {}), _jsx(EmbedFormField, { container: embedFormFieldContainer })] }))] }));
37
52
  }
38
53
  export default QandaContainer;
@@ -6,6 +6,7 @@ import { openForm, closeForm, updateQuestion, updateDisplayNameError, } from '..
6
6
  import { QandaContext } from '../QandaProvider';
7
7
  import { usePostQuestionMutation } from '../../services/comments-api';
8
8
  import Overlay from '../Overlay';
9
+ import Loader from '../Loader';
9
10
  import { Button } from '@financial-times/o3-button';
10
11
  import MessageBox from '../Overlay/MessageBox';
11
12
  import { getUserProfile } from '../../store/user';
@@ -145,5 +146,5 @@ export default function QuestionForm() {
145
146
  },
146
147
  // @ts-expect-error - o3 Button is currently accepting a JSX element as label
147
148
  // but the type definition needs to be updated by Origami team
148
- label: isLoading ? (_jsx("div", { class: "o-loading o-loading--light o-loading--small" })) : ('Submit your question'), type: "primary", theme: "mono" }), !profile?.token && (_jsx("span", { className: "o3-type-detail", children: "Submitting a question will create a community profile with FT." }))] }) })), isSuccess && (_jsx(MessageBox, { color: "#00572C", styles: "o-message--alert o-message--success", title: "Question sent", text: "Your question is in the queue and will be reviewed by our editors." })), error && (_jsx(MessageBox, { color: "var(--o3-color-use-case-error)", styles: "o-message--alert o-message--error", title: "Connection issue", text: "Could not submit question: [error messages to come in another ticket]" })), _jsx(MessageBox, { styles: "o-message--notice o-message--inform", title: getTitleByUser(profile), text: getTextByUser(profile) })] }));
149
+ label: isLoading ? _jsx(Loader, {}) : 'Submit your question', type: "primary", theme: "mono" }), !profile?.token && (_jsx("span", { className: "o3-type-detail", children: "Submitting a question will create a community profile with FT." }))] }) })), isSuccess && (_jsx(MessageBox, { color: "#00572C", styles: "o-message--alert o-message--success", title: "Question sent", text: "Your question is in the queue and will be reviewed by our editors." })), error && (_jsx(MessageBox, { color: "var(--o3-color-use-case-error)", styles: "o-message--alert o-message--error", title: "Connection issue", text: "Could not submit question: [error messages to come in another ticket]" })), _jsx(MessageBox, { styles: "o-message--notice o-message--inform", title: getTitleByUser(profile), text: getTextByUser(profile) })] }));
149
150
  }
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { useEffect, useContext } from 'react';
3
+ import { useSelector } from 'react-redux';
4
+ import QandaBlock from '../QandaBlock/index';
5
+ import { QandaContext } from '../QandaProvider';
6
+ import Loader from '../Loader';
7
+ import { useGetQandAStreamQuery } from '../../services/comments-api';
8
+ function Stream() {
9
+ const latestAnsweredQuestionId = useSelector((state) => state.stream.latestAnsweredQuestionId);
10
+ const { storyId, useStaging, commentsAPIUrl } = useContext(QandaContext);
11
+ const { data, error, isLoading } = useGetQandAStreamQuery({
12
+ storyId,
13
+ useStaging,
14
+ commentsAPIUrl,
15
+ });
16
+ useEffect(() => {
17
+ const element = document.getElementById(latestAnsweredQuestionId);
18
+ element?.scrollIntoView({ behavior: 'smooth' });
19
+ }, [latestAnsweredQuestionId]);
20
+ return (_jsxs("section", { className: "qanda__stream", children: [_jsx("h2", { className: "o3-type-title-md qanda__stream-title", children: data && data.status && data.status === 'pre-live'
21
+ ? 'Top questions so far'
22
+ : 'What our readers are asking' }), _jsxs("div", { className: "qanda__blocks-container", "data-testid": "qanda__blocks-container", id: "qanda__blocks-container", "aria-busy": isLoading, children: [isLoading && (_jsx(Loader, { theme: "dark", extraClassNames: "qanda__stream-loader" })), error && _jsx("p", { children: "Failed to load Q&A stream" }), data &&
23
+ data.qandas &&
24
+ data.qandas.map((qandaBlock) => (_jsx(QandaBlock, { id: qandaBlock.id, body: qandaBlock.body, type: "comment", publishedDate: qandaBlock.publishedDate, author: qandaBlock.author, byline: qandaBlock.byline, showAnswerTime: data.status === 'live', children: qandaBlock.children }, qandaBlock.id)))] })] }));
25
+ }
26
+ export default Stream;
@@ -98,6 +98,12 @@ export const nextCommentsApi = createApi({
98
98
  Authorization: `Bearer ${token}`,
99
99
  },
100
100
  }),
101
+ transformResponse: (response, meta, arg) => {
102
+ const allowedRoles = ['ADMIN', 'MODERATOR'];
103
+ const { isExpert, isStaffExpert, role } = response;
104
+ const expertView = isExpert || isStaffExpert || !!(role && allowedRoles.includes(role));
105
+ return { expertView };
106
+ },
101
107
  }),
102
108
  // POST A QUESTION
103
109
  postQuestion: builder.mutation({
@@ -0,0 +1,22 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ const initialState = {
3
+ isExpertFormOpen: false,
4
+ expertView: undefined,
5
+ };
6
+ export const expertSlice = createSlice({
7
+ name: 'expert',
8
+ initialState,
9
+ reducers: {
10
+ openExpertForm: (state) => {
11
+ state.isExpertFormOpen = true;
12
+ },
13
+ closeExpertForm: (state) => {
14
+ state.isExpertFormOpen = false;
15
+ },
16
+ setExpertView: (state, action) => {
17
+ state.expertView = action.payload;
18
+ },
19
+ },
20
+ });
21
+ export const { openExpertForm, closeExpertForm, setExpertView } = expertSlice.actions;
22
+ export default expertSlice.reducer;
@@ -3,12 +3,14 @@ import { setupListeners } from '@reduxjs/toolkit/query';
3
3
  import questionReducer from './questionSlice';
4
4
  import { stream } from './stream';
5
5
  import { user } from './user';
6
+ import expertReducer from './expertSlice';
6
7
  import { nextCommentsApi } from '../services/comments-api';
7
8
  import { rtkQueryErrorLogger } from '../services/error-middleware';
8
9
  export const rootReducer = combineReducers({
9
10
  stream: stream.reducer,
10
11
  question: questionReducer,
11
12
  user: user.reducer,
13
+ expert: expertReducer,
12
14
  nextCommentsApi: nextCommentsApi.reducer,
13
15
  });
14
16
  export const store = configureStore({
package/dist/qanda.scss CHANGED
@@ -6,7 +6,6 @@ $app-header-color: #007acc;
6
6
  @import '@financial-times/o-forms/main';
7
7
  @import '@financial-times/o-icons/main';
8
8
  @import '@financial-times/o-labels/main';
9
- @import '@financial-times/o-loading/main';
10
9
  @import '@financial-times/o-message/main';
11
10
  @import '@financial-times/o3-foundation/css/core.css';
12
11
  /* Inlined from ../components/Author/styles.css */
@@ -311,6 +310,24 @@ $app-header-color: #007acc;
311
310
  margin-top: var(--o3-spacing-2xs);
312
311
  }
313
312
 
313
+ /* Inlined from ../components/Loader/styles.scss */
314
+ @import '@financial-times/o-loading/main';
315
+
316
+ @include oLoading(
317
+ $opts: (
318
+ 'themes': (
319
+ 'light',
320
+ 'dark',
321
+ ),
322
+ 'sizes': (
323
+ 'mini',
324
+ 'small',
325
+ 'medium',
326
+ 'large',
327
+ ),
328
+ )
329
+ );
330
+
314
331
  /* Inlined from ../components/Overlay/styles.css */
315
332
  @import '@financial-times/o3-button/css/core.css';
316
333
 
@@ -365,6 +382,10 @@ $app-header-color: #007acc;
365
382
  background: var(--o3-color-palette-black-90);
366
383
  }
367
384
 
385
+ .overlay__outer--no-background {
386
+ background: none;
387
+ }
388
+
368
389
  .overlay__inner {
369
390
  border-radius: var(--o3-spacing-2xs) var(--o3-spacing-2xs) 0 0;
370
391
  background: var(--o3-color-use-case-page-background);
@@ -383,6 +404,15 @@ $app-header-color: #007acc;
383
404
  }
384
405
  }
385
406
 
407
+ .overlay__inner--shadow {
408
+ @media (min-width: 740px) {
409
+ width: 420px;
410
+ box-shadow:
411
+ -4px 6px 10px 0 rgba(0, 0, 0, 0.15),
412
+ -4px 4px 4px 0 rgba(0, 0, 0, 0.25);
413
+ }
414
+ }
415
+
386
416
  .overlay__header {
387
417
  border-bottom: 1px solid var(--o3-color-palette-black-10);
388
418
  display: flex;
@@ -493,15 +523,6 @@ $app-header-color: #007acc;
493
523
  }
494
524
 
495
525
  .qanda-container {
496
- .qanda__blocks-container {
497
- max-width: 1220px;
498
- margin: auto;
499
- display: flex;
500
- flex-direction: column;
501
- gap: var(--o3-spacing-l);
502
- margin-bottom: var(--o3-spacing-l);
503
- }
504
-
505
526
  .qanda-block {
506
527
  display: flex;
507
528
  flex-direction: column;
@@ -652,6 +673,32 @@ $app-header-color: #007acc;
652
673
  }
653
674
  }
654
675
 
676
+ /* Inlined from ../components/Stream/styles.css */
677
+ @import '@financial-times/o3-foundation/css/core.css';
678
+
679
+ .qanda-container {
680
+ .qanda__stream {
681
+ margin-top: var(--o3-spacing-l);
682
+ }
683
+
684
+ .qanda__stream-title {
685
+ margin: 0 0 var(--o3-spacing-s);
686
+ }
687
+
688
+ .qanda__stream-loader {
689
+ align-self: center;
690
+ }
691
+
692
+ .qanda__blocks-container {
693
+ max-width: 1220px;
694
+ margin: auto;
695
+ display: flex;
696
+ flex-direction: column;
697
+ gap: var(--o3-spacing-l);
698
+ margin-bottom: var(--o3-spacing-l);
699
+ }
700
+ }
701
+
655
702
 
656
703
  @include oForms(
657
704
  $opts: (
@@ -677,17 +724,6 @@ $app-header-color: #007acc;
677
724
  )
678
725
  );
679
726
 
680
- @include oLoading(
681
- $opts: (
682
- 'themes': (
683
- 'light',
684
- ),
685
- 'sizes': (
686
- 'small',
687
- ),
688
- )
689
- );
690
-
691
727
  @include oMessage(
692
728
  $opts: (
693
729
  'types': (
@@ -1,5 +1,5 @@
1
1
  type EmbedFormFieldProps = {
2
2
  container?: HTMLElement;
3
3
  };
4
- declare function EmbedFormField({ container }: EmbedFormFieldProps): import("react").JSX.Element;
4
+ declare function EmbedFormField({ container }: EmbedFormFieldProps): import("react").JSX.Element | null;
5
5
  export default EmbedFormField;
@@ -0,0 +1,2 @@
1
+ declare function ExpertDrawer(): import("react").JSX.Element | null;
2
+ export default ExpertDrawer;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Loader component that shows the o-loading spinner, defaults to small light variant
3
+ * @typedef {Object} LoaderProps
4
+ * @property {('mini'|'small'|'medium'|'large')} [size] - Size variant of the loader.
5
+ * @property {('dark'|'light')} [theme] - Theme variant of the loader.
6
+
7
+ * @property {string} [extraClassNames] - Additional CSS class names to be applied to the loader component for custom styling.
8
+ */
9
+ export type LoaderProps = {
10
+ size?: 'mini' | 'small' | 'medium' | 'large';
11
+ theme?: 'dark' | 'light';
12
+ extraClassNames?: string;
13
+ };
14
+ export default function Loader({ size, theme, extraClassNames, }: LoaderProps): import("react-dom/src").JSX.Element;
@@ -1,7 +1,12 @@
1
+ type OverlayOptions = {
2
+ noBackground?: boolean;
3
+ shadow?: boolean;
4
+ };
1
5
  type OverlayProps = {
2
6
  title: String;
3
7
  children: React.ReactNode;
4
8
  closeCallback: () => void;
9
+ options?: OverlayOptions;
5
10
  };
6
- declare function Overlay({ title, children, closeCallback }: OverlayProps): import("react-dom/src").JSX.Element;
11
+ declare function Overlay({ title, children, closeCallback, options, }: OverlayProps): import("react-dom/src").JSX.Element;
7
12
  export default Overlay;
@@ -30,10 +30,13 @@ type CachedData = {
30
30
  updateQA?: QA;
31
31
  [key: string]: any;
32
32
  };
33
+ type UserRolesTransformed = {
34
+ expertView: boolean;
35
+ };
33
36
  export declare const nextCommentsApi: import("@reduxjs/toolkit/query").Api<import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, {
34
37
  getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, CommentsStreamTransformed, "nextCommentsApi">;
35
38
  getQandAUpdates: import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, CachedData, "nextCommentsApi">;
36
- getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">;
39
+ getUserRoles: import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">;
37
40
  postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<CommentsAPIUrlQuestionParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">;
38
41
  }, "nextCommentsApi", never, typeof import("@reduxjs/toolkit/query").coreModuleName | typeof import("@reduxjs/toolkit/query/react").reactHooksModuleName>;
39
42
  export declare const useGetQandAStreamQuery: <R extends Record<string, any> = import("@reduxjs/toolkit/query").TSHelpersId<(Omit<{
@@ -310,7 +313,7 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
310
313
  startedTimeStamp?: undefined | undefined;
311
314
  fulfilledTimeStamp?: undefined | undefined;
312
315
  } & {
313
- currentData?: any;
316
+ currentData?: UserRolesTransformed | undefined;
314
317
  isUninitialized: false;
315
318
  isLoading: false;
316
319
  isFetching: false;
@@ -318,8 +321,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
318
321
  isError: false;
319
322
  }, "isUninitialized"> & {
320
323
  isUninitialized: true;
321
- }) | import("@reduxjs/toolkit/query").TSHelpersOverride<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
322
- currentData?: any;
324
+ }) | import("@reduxjs/toolkit/query").TSHelpersOverride<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
325
+ currentData?: UserRolesTransformed | undefined;
323
326
  isUninitialized: false;
324
327
  isLoading: false;
325
328
  isFetching: false;
@@ -333,8 +336,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
333
336
  isSuccess: true;
334
337
  isFetching: true;
335
338
  error: undefined;
336
- } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
337
- currentData?: any;
339
+ } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
340
+ currentData?: UserRolesTransformed | undefined;
338
341
  isUninitialized: false;
339
342
  isLoading: false;
340
343
  isFetching: false;
@@ -344,8 +347,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
344
347
  isSuccess: true;
345
348
  isFetching: false;
346
349
  error: undefined;
347
- } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
348
- currentData?: any;
350
+ } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
351
+ currentData?: UserRolesTransformed | undefined;
349
352
  isUninitialized: false;
350
353
  isLoading: false;
351
354
  isFetching: false;
@@ -353,8 +356,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
353
356
  isError: false;
354
357
  }, "data" | "fulfilledTimeStamp" | "currentData">>) | ({
355
358
  isError: true;
356
- } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
357
- currentData?: any;
359
+ } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
360
+ currentData?: UserRolesTransformed | undefined;
358
361
  isUninitialized: false;
359
362
  isLoading: false;
360
363
  isFetching: false;
@@ -377,7 +380,7 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
377
380
  startedTimeStamp?: undefined | undefined;
378
381
  fulfilledTimeStamp?: undefined | undefined;
379
382
  } & {
380
- currentData?: any;
383
+ currentData?: UserRolesTransformed | undefined;
381
384
  isUninitialized: false;
382
385
  isLoading: false;
383
386
  isFetching: false;
@@ -385,8 +388,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
385
388
  isError: false;
386
389
  }, "isUninitialized"> & {
387
390
  isUninitialized: true;
388
- }) | import("@reduxjs/toolkit/query").TSHelpersOverride<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
389
- currentData?: any;
391
+ }) | import("@reduxjs/toolkit/query").TSHelpersOverride<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
392
+ currentData?: UserRolesTransformed | undefined;
390
393
  isUninitialized: false;
391
394
  isLoading: false;
392
395
  isFetching: false;
@@ -400,8 +403,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
400
403
  isSuccess: true;
401
404
  isFetching: true;
402
405
  error: undefined;
403
- } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
404
- currentData?: any;
406
+ } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
407
+ currentData?: UserRolesTransformed | undefined;
405
408
  isUninitialized: false;
406
409
  isLoading: false;
407
410
  isFetching: false;
@@ -411,8 +414,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
411
414
  isSuccess: true;
412
415
  isFetching: false;
413
416
  error: undefined;
414
- } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
415
- currentData?: any;
417
+ } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
418
+ currentData?: UserRolesTransformed | undefined;
416
419
  isUninitialized: false;
417
420
  isLoading: false;
418
421
  isFetching: false;
@@ -420,8 +423,8 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
420
423
  isError: false;
421
424
  }, "data" | "fulfilledTimeStamp" | "currentData">>) | ({
422
425
  isError: true;
423
- } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">> & {
424
- currentData?: any;
426
+ } & Required<Pick<import("@reduxjs/toolkit/query").QuerySubState<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">> & {
427
+ currentData?: UserRolesTransformed | undefined;
425
428
  isUninitialized: false;
426
429
  isLoading: false;
427
430
  isFetching: false;
@@ -431,7 +434,7 @@ export declare const useGetQandAStreamQuery: <R extends Record<string, any> = im
431
434
  status: import("@reduxjs/toolkit/query").QueryStatus;
432
435
  }) => R) | undefined;
433
436
  }) | undefined) => [R][R extends any ? 0 : never] & {
434
- refetch: () => import("@reduxjs/toolkit/query").QueryActionCreatorResult<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">>;
437
+ refetch: () => import("@reduxjs/toolkit/query").QueryActionCreatorResult<import("@reduxjs/toolkit/query").QueryDefinition<CommentsAPIUrlTokenParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, UserRolesTransformed, "nextCommentsApi">>;
435
438
  }, usePostQuestionMutation: <R extends Record<string, any> = ({
436
439
  requestId?: undefined;
437
440
  status: import("@reduxjs/toolkit/query").QueryStatus.uninitialized;
@@ -0,0 +1,15 @@
1
+ export interface ExpertState {
2
+ isExpertFormOpen: boolean;
3
+ expertView?: boolean;
4
+ }
5
+ export declare const expertSlice: import("@reduxjs/toolkit").Slice<ExpertState, {
6
+ openExpertForm: (state: import("immer").WritableDraft<ExpertState>) => void;
7
+ closeExpertForm: (state: import("immer").WritableDraft<ExpertState>) => void;
8
+ setExpertView: (state: import("immer").WritableDraft<ExpertState>, action: {
9
+ payload: any;
10
+ type: string;
11
+ }) => void;
12
+ }, "expert", "expert", import("@reduxjs/toolkit").SliceSelectors<ExpertState>>;
13
+ export declare const openExpertForm: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expert/openExpertForm">, closeExpertForm: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expert/closeExpertForm">, setExpertView: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "expert/setExpertView">;
14
+ declare const _default: import("redux").Reducer<ExpertState>;
15
+ export default _default;
@@ -2,10 +2,12 @@ import { ThunkAction, Action } from '@reduxjs/toolkit';
2
2
  import { QuestionState } from './questionSlice';
3
3
  import { StreamState } from './stream';
4
4
  import { UserState } from './user';
5
+ import { ExpertState } from './expertSlice';
5
6
  export declare const rootReducer: import("redux").Reducer<{
6
7
  stream: StreamState;
7
8
  question: QuestionState;
8
9
  user: UserState;
10
+ expert: ExpertState;
9
11
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
10
12
  getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
11
13
  storyId: string;
@@ -35,7 +37,9 @@ export declare const rootReducer: import("redux").Reducer<{
35
37
  commentsAPIUrl: string;
36
38
  } & {
37
39
  token: string;
38
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">;
40
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
41
+ expertView: boolean;
42
+ }, "nextCommentsApi">;
39
43
  postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
40
44
  storyId: string;
41
45
  useStaging: boolean;
@@ -51,6 +55,7 @@ export declare const rootReducer: import("redux").Reducer<{
51
55
  stream: StreamState | undefined;
52
56
  question: QuestionState | undefined;
53
57
  user: UserState | undefined;
58
+ expert: ExpertState | undefined;
54
59
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
55
60
  getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
56
61
  storyId: string;
@@ -80,7 +85,9 @@ export declare const rootReducer: import("redux").Reducer<{
80
85
  commentsAPIUrl: string;
81
86
  } & {
82
87
  token: string;
83
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">;
88
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
89
+ expertView: boolean;
90
+ }, "nextCommentsApi">;
84
91
  postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
85
92
  storyId: string;
86
93
  useStaging: boolean;
@@ -97,11 +104,13 @@ export type RootState = {
97
104
  stream: StreamState;
98
105
  question: QuestionState;
99
106
  user: UserState;
107
+ expert: ExpertState;
100
108
  };
101
109
  export declare const store: import("@reduxjs/toolkit").EnhancedStore<{
102
110
  stream: StreamState;
103
111
  question: QuestionState;
104
112
  user: UserState;
113
+ expert: ExpertState;
105
114
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
106
115
  getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
107
116
  storyId: string;
@@ -131,7 +140,9 @@ export declare const store: import("@reduxjs/toolkit").EnhancedStore<{
131
140
  commentsAPIUrl: string;
132
141
  } & {
133
142
  token: string;
134
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">;
143
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
144
+ expertView: boolean;
145
+ }, "nextCommentsApi">;
135
146
  postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
136
147
  storyId: string;
137
148
  useStaging: boolean;
@@ -148,6 +159,7 @@ export declare const store: import("@reduxjs/toolkit").EnhancedStore<{
148
159
  stream: StreamState;
149
160
  question: QuestionState;
150
161
  user: UserState;
162
+ expert: ExpertState;
151
163
  nextCommentsApi: import("@reduxjs/toolkit/query").CombinedState<{
152
164
  getQandAStream: import("@reduxjs/toolkit/query").QueryDefinition<{
153
165
  storyId: string;
@@ -177,7 +189,9 @@ export declare const store: import("@reduxjs/toolkit").EnhancedStore<{
177
189
  commentsAPIUrl: string;
178
190
  } & {
179
191
  token: string;
180
- }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, any, "nextCommentsApi">;
192
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@reduxjs/toolkit/query").FetchArgs, unknown, import("@reduxjs/toolkit/query").FetchBaseQueryError, {}, import("@reduxjs/toolkit/query").FetchBaseQueryMeta>, never, {
193
+ expertView: boolean;
194
+ }, "nextCommentsApi">;
181
195
  postQuestion: import("@reduxjs/toolkit/query").MutationDefinition<{
182
196
  storyId: string;
183
197
  useStaging: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/qanda-ui",
3
- "version": "0.0.1-beta.7",
3
+ "version": "0.0.1-beta.8",
4
4
  "description": "Components for the Live Q&A UI",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -1,43 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsx_runtime_1 = require("preact/jsx-runtime");
7
- const o_comments_1 = __importDefault(require("@financial-times/o-comments"));
8
- const react_1 = require("react");
9
- const react_redux_1 = require("react-redux");
10
- const QandaProvider_1 = require("./QandaProvider");
11
- const comments_api_1 = require("../services/comments-api");
12
- const reliability_1 = require("../services/reliability");
13
- function QandaOComments() {
14
- const allowedRoles = ['ADMIN', 'MODERATOR'];
15
- const { storyId, useStaging, commentsAPIUrl, title } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
16
- const profile = (0, react_redux_1.useSelector)((state) => state.user.profile);
17
- if (!profile) {
18
- return null;
19
- }
20
- const { data: userRole, error, isLoading, } = (0, comments_api_1.useGetUserRolesQuery)({
21
- storyId,
22
- useStaging,
23
- commentsAPIUrl,
24
- token: profile.token,
25
- }, { skip: !profile.token });
26
- (0, react_1.useEffect)(() => {
27
- if (!userRole) {
28
- return;
29
- }
30
- const { isExpert, isStaffExpert, role } = userRole;
31
- const showCoral = isExpert || isStaffExpert || (role && allowedRoles.includes(role));
32
- if (showCoral) {
33
- try {
34
- o_comments_1.default.init();
35
- }
36
- catch (error) {
37
- reliability_1.qandaReliability.failure('expert-load-coral-ui', error);
38
- }
39
- }
40
- }, [userRole]);
41
- return ((0, jsx_runtime_1.jsx)("div", { className: "o-comments", id: "o-comments-stream", "data-o-component": "o-comments", "data-o-comments-article-id": storyId, "data-o-comments-article-url": `https://www.ft.com/content/${storyId}`, "data-o-comments-use-staging-environment": useStaging, "data-o-comments-title": title }));
42
- }
43
- exports.default = QandaOComments;
@@ -1,28 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsx_runtime_1 = require("preact/jsx-runtime");
7
- const react_1 = require("react");
8
- const react_redux_1 = require("react-redux");
9
- const index_1 = __importDefault(require("./QandaBlock/index"));
10
- const QandaProvider_1 = require("./QandaProvider");
11
- const comments_api_1 = require("../services/comments-api");
12
- function Stream() {
13
- const latestAnsweredQuestionId = (0, react_redux_1.useSelector)((state) => state.stream.latestAnsweredQuestionId);
14
- const { storyId, useStaging, commentsAPIUrl } = (0, react_1.useContext)(QandaProvider_1.QandaContext);
15
- const { data, error, isLoading } = (0, comments_api_1.useGetQandAStreamQuery)({
16
- storyId,
17
- useStaging,
18
- commentsAPIUrl,
19
- });
20
- (0, react_1.useEffect)(() => {
21
- const element = document.getElementById(latestAnsweredQuestionId);
22
- element?.scrollIntoView({ behavior: 'smooth' });
23
- }, [latestAnsweredQuestionId]);
24
- return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda__blocks-container", "data-testid": "qanda__blocks-container", id: "qanda__blocks-container", children: [isLoading && (0, jsx_runtime_1.jsx)("p", { children: "Loading Stream for you, be patient..." }), error && (0, jsx_runtime_1.jsx)("p", { children: "Failed to load Q&A stream" }), data &&
25
- data.qandas &&
26
- data.qandas.map((qandaBlock) => ((0, jsx_runtime_1.jsx)(index_1.default, { id: qandaBlock.id, body: qandaBlock.body, type: "comment", publishedDate: qandaBlock.publishedDate, author: qandaBlock.author, byline: qandaBlock.byline, showAnswerTime: data.status === 'live', children: qandaBlock.children }, qandaBlock.id)))] }));
27
- }
28
- exports.default = Stream;
@@ -1,38 +0,0 @@
1
- import { jsx as _jsx } from "preact/jsx-runtime";
2
- import OComments from '@financial-times/o-comments';
3
- import { useEffect, useContext } from 'react';
4
- import { useSelector } from 'react-redux';
5
- import { QandaContext } from './QandaProvider';
6
- import { useGetUserRolesQuery } from '../services/comments-api';
7
- import { qandaReliability } from '../services/reliability';
8
- function QandaOComments() {
9
- const allowedRoles = ['ADMIN', 'MODERATOR'];
10
- const { storyId, useStaging, commentsAPIUrl, title } = useContext(QandaContext);
11
- const profile = useSelector((state) => state.user.profile);
12
- if (!profile) {
13
- return null;
14
- }
15
- const { data: userRole, error, isLoading, } = useGetUserRolesQuery({
16
- storyId,
17
- useStaging,
18
- commentsAPIUrl,
19
- token: profile.token,
20
- }, { skip: !profile.token });
21
- useEffect(() => {
22
- if (!userRole) {
23
- return;
24
- }
25
- const { isExpert, isStaffExpert, role } = userRole;
26
- const showCoral = isExpert || isStaffExpert || (role && allowedRoles.includes(role));
27
- if (showCoral) {
28
- try {
29
- OComments.init();
30
- }
31
- catch (error) {
32
- qandaReliability.failure('expert-load-coral-ui', error);
33
- }
34
- }
35
- }, [userRole]);
36
- return (_jsx("div", { className: "o-comments", id: "o-comments-stream", "data-o-component": "o-comments", "data-o-comments-article-id": storyId, "data-o-comments-article-url": `https://www.ft.com/content/${storyId}`, "data-o-comments-use-staging-environment": useStaging, "data-o-comments-title": title }));
37
- }
38
- export default QandaOComments;
@@ -1,23 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { useEffect, useContext } from 'react';
3
- import { useSelector } from 'react-redux';
4
- import QandaBlock from './QandaBlock/index';
5
- import { QandaContext } from './QandaProvider';
6
- import { useGetQandAStreamQuery } from '../services/comments-api';
7
- function Stream() {
8
- const latestAnsweredQuestionId = useSelector((state) => state.stream.latestAnsweredQuestionId);
9
- const { storyId, useStaging, commentsAPIUrl } = useContext(QandaContext);
10
- const { data, error, isLoading } = useGetQandAStreamQuery({
11
- storyId,
12
- useStaging,
13
- commentsAPIUrl,
14
- });
15
- useEffect(() => {
16
- const element = document.getElementById(latestAnsweredQuestionId);
17
- element?.scrollIntoView({ behavior: 'smooth' });
18
- }, [latestAnsweredQuestionId]);
19
- return (_jsxs("div", { className: "qanda__blocks-container", "data-testid": "qanda__blocks-container", id: "qanda__blocks-container", children: [isLoading && _jsx("p", { children: "Loading Stream for you, be patient..." }), error && _jsx("p", { children: "Failed to load Q&A stream" }), data &&
20
- data.qandas &&
21
- data.qandas.map((qandaBlock) => (_jsx(QandaBlock, { id: qandaBlock.id, body: qandaBlock.body, type: "comment", publishedDate: qandaBlock.publishedDate, author: qandaBlock.author, byline: qandaBlock.byline, showAnswerTime: data.status === 'live', children: qandaBlock.children }, qandaBlock.id)))] }));
22
- }
23
- export default Stream;
@@ -1,2 +0,0 @@
1
- declare function QandaOComments(): import("react").JSX.Element | null;
2
- export default QandaOComments;