@financial-times/qanda-ui 0.0.1-beta.7 → 0.0.1-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/EmbedFormField/index.js +4 -0
- package/dist/cjs/components/ExpertDrawer/index.js +36 -0
- package/dist/cjs/components/FloatingActionBar/index.js +20 -5
- package/dist/cjs/components/Loader/index.js +7 -0
- package/dist/cjs/components/Overlay/index.js +12 -2
- package/dist/cjs/components/QandaBlock/index.js +58 -0
- package/dist/cjs/components/QandaContainer.js +17 -2
- package/dist/cjs/components/QuestionForm/index.js +2 -1
- package/dist/cjs/components/Stream/index.js +31 -0
- package/dist/cjs/services/comments-api.js +6 -0
- package/dist/cjs/store/expertSlice.js +26 -0
- package/dist/cjs/store/index.js +2 -0
- package/dist/esm/components/EmbedFormField/index.js +5 -1
- package/dist/esm/components/ExpertDrawer/index.js +31 -0
- package/dist/esm/components/FloatingActionBar/index.js +21 -6
- package/dist/esm/components/Loader/index.js +4 -0
- package/dist/esm/components/Overlay/index.js +9 -2
- package/dist/esm/components/QandaBlock/index.js +58 -0
- package/dist/esm/components/QandaContainer.js +19 -4
- package/dist/esm/components/QuestionForm/index.js +2 -1
- package/dist/esm/components/Stream/index.js +26 -0
- package/dist/esm/services/comments-api.js +6 -0
- package/dist/esm/store/expertSlice.js +22 -0
- package/dist/esm/store/index.js +2 -0
- package/dist/qanda.scss +59 -21
- package/dist/types/components/EmbedFormField/index.d.ts +1 -1
- package/dist/types/components/ExpertDrawer/index.d.ts +2 -0
- package/dist/types/components/Loader/index.d.ts +14 -0
- package/dist/types/components/Overlay/index.d.ts +6 -1
- package/dist/types/components/QandaBlock/index.d.ts +1 -1
- package/dist/types/services/comments-api.d.ts +23 -20
- package/dist/types/store/expertSlice.d.ts +15 -0
- package/dist/types/store/index.d.ts +18 -4
- package/package.json +1 -1
- package/dist/cjs/components/QandaOComments.js +0 -43
- package/dist/cjs/components/Stream.js +0 -28
- package/dist/esm/components/QandaOComments.js +0 -38
- package/dist/esm/components/Stream.js +0 -23
- package/dist/types/components/QandaOComments.d.ts +0 -2
- /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:
|
|
31
|
-
'data-trackable':
|
|
32
|
-
'data-trackable-context-action':
|
|
33
|
-
'data-testid':
|
|
34
|
-
}, label:
|
|
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
|
-
|
|
6
|
-
|
|
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;
|
|
@@ -8,11 +8,69 @@ const cp_content_pipeline_ui_1 = require("@financial-times/cp-content-pipeline-u
|
|
|
8
8
|
require("../Expander/index");
|
|
9
9
|
const index_1 = __importDefault(require("../Expander/index"));
|
|
10
10
|
const Author_1 = __importDefault(require("../Author"));
|
|
11
|
+
const react_1 = require("react");
|
|
11
12
|
function renderTime(publishedDate) {
|
|
12
13
|
const date = new Date(publishedDate);
|
|
13
14
|
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
14
15
|
}
|
|
16
|
+
// Get shadow color based on factor
|
|
17
|
+
function interpolateColor(factor, colorStart = 'ccc1b7', colorEnd = '4d4845', alphaStart = 0.5, alphaEnd = 0.85) {
|
|
18
|
+
const hexToRgb = (hex) => hex.match(/[0-9a-f]{2}/g).map((x) => parseInt(x, 16));
|
|
19
|
+
const startRgb = hexToRgb(colorStart);
|
|
20
|
+
const endRgb = hexToRgb(colorEnd);
|
|
21
|
+
const rgb = startRgb.map((val, i) => Math.round(val + (endRgb[i] - val) * factor));
|
|
22
|
+
const alpha = alphaStart + factor * (alphaEnd - alphaStart);
|
|
23
|
+
return `rgba(${rgb.join(', ')}, ${alpha})`;
|
|
24
|
+
}
|
|
15
25
|
function QandaBlock({ id, body, children, byline, subtitle, showAnswerTime = false, }) {
|
|
26
|
+
(0, react_1.useEffect)(() => {
|
|
27
|
+
let currentBubble = null;
|
|
28
|
+
const factorRef = { current: 0 };
|
|
29
|
+
let animationFrameId;
|
|
30
|
+
const observer = new IntersectionObserver((entries) => {
|
|
31
|
+
entries.forEach((entry) => {
|
|
32
|
+
if (entry.isIntersecting) {
|
|
33
|
+
if (currentBubble !== entry.target) {
|
|
34
|
+
factorRef.current = 0;
|
|
35
|
+
}
|
|
36
|
+
currentBubble = entry.target;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}, {
|
|
40
|
+
root: null,
|
|
41
|
+
rootMargin: '-50% 0px -50% 0px',
|
|
42
|
+
threshold: 0,
|
|
43
|
+
});
|
|
44
|
+
const comments = document.querySelectorAll('.qanda-block__comment');
|
|
45
|
+
comments.forEach((comment) => observer.observe(comment));
|
|
46
|
+
const animate = () => {
|
|
47
|
+
const centerY = window.innerHeight / 2;
|
|
48
|
+
if (currentBubble) {
|
|
49
|
+
const rect = currentBubble.getBoundingClientRect();
|
|
50
|
+
const commentCenterY = rect.top + rect.height / 2;
|
|
51
|
+
const distanceRatio = Math.min(Math.abs(centerY - commentCenterY) / centerY, 1);
|
|
52
|
+
const targetFactor = (1 - distanceRatio) ** 3;
|
|
53
|
+
factorRef.current += (targetFactor - factorRef.current) * 0.1;
|
|
54
|
+
const scale = 1 + 0.04 * factorRef.current;
|
|
55
|
+
const shadow = interpolateColor(factorRef.current);
|
|
56
|
+
currentBubble.style.transform = `scale(${scale})`;
|
|
57
|
+
currentBubble.style.filter = `drop-shadow(0px 2px 8px ${shadow})`;
|
|
58
|
+
if (factorRef.current < 0.001 && distanceRatio > 0.95) {
|
|
59
|
+
currentBubble.removeAttribute('style');
|
|
60
|
+
currentBubble = null;
|
|
61
|
+
factorRef.current = 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
65
|
+
};
|
|
66
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
67
|
+
window.addEventListener('resize', animate);
|
|
68
|
+
return () => {
|
|
69
|
+
cancelAnimationFrame(animationFrameId);
|
|
70
|
+
window.removeEventListener('resize', animate);
|
|
71
|
+
observer.disconnect();
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
16
74
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-block", children: [(0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__comment qanda-block__question", id: id, "data-testid": `qanda-block__question-${id}`, "data-trackable": "liveqa_question", children: [subtitle && ((0, jsx_runtime_1.jsx)("div", { className: "o3-type-headline-sm qanda-block__question-subtitle", children: subtitle })), (0, jsx_runtime_1.jsx)("div", { className: "o3-type-body-highlight qanda-block__comment-byline", children: (0, jsx_runtime_1.jsx)(cp_content_pipeline_ui_1.Byline, { structuredContent: byline, showEditedBy: false }) }), (0, jsx_runtime_1.jsx)("div", { className: "o3-type-body-base qanda-block__comment-text", children: (0, jsx_runtime_1.jsx)(index_1.default, { className: "qanda-block__text", expandLabel: "Expand question", collapseLabel: "Collapse question", id: id, children: (0, jsx_runtime_1.jsx)(cp_content_pipeline_ui_1.RichText, { structuredContent: body.structured }) }) })] }, id), children?.map((child) => child.body && ((0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__comment qanda-block__answer", "data-testid": `qanda-block__answer-${child.id}`, "data-trackable": "liveqa_answer", children: [(0, jsx_runtime_1.jsxs)("div", { className: "qanda-block__answer-header", children: [(0, jsx_runtime_1.jsx)("div", { className: "o3-type-body-highlight qanda-block__comment-byline", children: child.author ? ((0, jsx_runtime_1.jsx)(Author_1.default, { byline: child.byline, headshot: child.author.headshot, prefLabel: child.author.prefLabel, role: child.author.role, streamPage: child.author.streamPage })) : ((0, jsx_runtime_1.jsx)(cp_content_pipeline_ui_1.Byline, { structuredContent: child.byline, showEditedBy: false })) }), showAnswerTime && ((0, jsx_runtime_1.jsx)("span", { className: "o3-type-detail", children: renderTime(child.publishedDate) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "o3-type-body-base qanda-block__comment-text", children: (0, jsx_runtime_1.jsx)(index_1.default, { className: "qanda-block__text", expandLabel: "Expand answer", collapseLabel: "Collapse answer", id: id, children: (0, jsx_runtime_1.jsx)(cp_content_pipeline_ui_1.RichText, { structuredContent: child.body.structured }) }) })] }, child.id)))] }));
|
|
17
75
|
}
|
|
18
76
|
exports.default = QandaBlock;
|
|
@@ -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
|
|
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.
|
|
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 ? (
|
|
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;
|
package/dist/cjs/store/index.js
CHANGED
|
@@ -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:
|
|
29
|
-
'data-trackable':
|
|
30
|
-
'data-trackable-context-action':
|
|
31
|
-
'data-testid':
|
|
32
|
-
}, label:
|
|
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
|
-
|
|
4
|
-
|
|
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;
|
|
@@ -3,11 +3,69 @@ import { RichText, Byline } from '@financial-times/cp-content-pipeline-ui';
|
|
|
3
3
|
import '../Expander/index';
|
|
4
4
|
import Expander from '../Expander/index';
|
|
5
5
|
import Author from '../Author';
|
|
6
|
+
import { useEffect } from 'react';
|
|
6
7
|
function renderTime(publishedDate) {
|
|
7
8
|
const date = new Date(publishedDate);
|
|
8
9
|
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
9
10
|
}
|
|
11
|
+
// Get shadow color based on factor
|
|
12
|
+
function interpolateColor(factor, colorStart = 'ccc1b7', colorEnd = '4d4845', alphaStart = 0.5, alphaEnd = 0.85) {
|
|
13
|
+
const hexToRgb = (hex) => hex.match(/[0-9a-f]{2}/g).map((x) => parseInt(x, 16));
|
|
14
|
+
const startRgb = hexToRgb(colorStart);
|
|
15
|
+
const endRgb = hexToRgb(colorEnd);
|
|
16
|
+
const rgb = startRgb.map((val, i) => Math.round(val + (endRgb[i] - val) * factor));
|
|
17
|
+
const alpha = alphaStart + factor * (alphaEnd - alphaStart);
|
|
18
|
+
return `rgba(${rgb.join(', ')}, ${alpha})`;
|
|
19
|
+
}
|
|
10
20
|
function QandaBlock({ id, body, children, byline, subtitle, showAnswerTime = false, }) {
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let currentBubble = null;
|
|
23
|
+
const factorRef = { current: 0 };
|
|
24
|
+
let animationFrameId;
|
|
25
|
+
const observer = new IntersectionObserver((entries) => {
|
|
26
|
+
entries.forEach((entry) => {
|
|
27
|
+
if (entry.isIntersecting) {
|
|
28
|
+
if (currentBubble !== entry.target) {
|
|
29
|
+
factorRef.current = 0;
|
|
30
|
+
}
|
|
31
|
+
currentBubble = entry.target;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}, {
|
|
35
|
+
root: null,
|
|
36
|
+
rootMargin: '-50% 0px -50% 0px',
|
|
37
|
+
threshold: 0,
|
|
38
|
+
});
|
|
39
|
+
const comments = document.querySelectorAll('.qanda-block__comment');
|
|
40
|
+
comments.forEach((comment) => observer.observe(comment));
|
|
41
|
+
const animate = () => {
|
|
42
|
+
const centerY = window.innerHeight / 2;
|
|
43
|
+
if (currentBubble) {
|
|
44
|
+
const rect = currentBubble.getBoundingClientRect();
|
|
45
|
+
const commentCenterY = rect.top + rect.height / 2;
|
|
46
|
+
const distanceRatio = Math.min(Math.abs(centerY - commentCenterY) / centerY, 1);
|
|
47
|
+
const targetFactor = (1 - distanceRatio) ** 3;
|
|
48
|
+
factorRef.current += (targetFactor - factorRef.current) * 0.1;
|
|
49
|
+
const scale = 1 + 0.04 * factorRef.current;
|
|
50
|
+
const shadow = interpolateColor(factorRef.current);
|
|
51
|
+
currentBubble.style.transform = `scale(${scale})`;
|
|
52
|
+
currentBubble.style.filter = `drop-shadow(0px 2px 8px ${shadow})`;
|
|
53
|
+
if (factorRef.current < 0.001 && distanceRatio > 0.95) {
|
|
54
|
+
currentBubble.removeAttribute('style');
|
|
55
|
+
currentBubble = null;
|
|
56
|
+
factorRef.current = 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
60
|
+
};
|
|
61
|
+
animationFrameId = requestAnimationFrame(animate);
|
|
62
|
+
window.addEventListener('resize', animate);
|
|
63
|
+
return () => {
|
|
64
|
+
cancelAnimationFrame(animationFrameId);
|
|
65
|
+
window.removeEventListener('resize', animate);
|
|
66
|
+
observer.disconnect();
|
|
67
|
+
};
|
|
68
|
+
}, []);
|
|
11
69
|
return (_jsxs("div", { className: "qanda-block", children: [_jsxs("div", { className: "qanda-block__comment qanda-block__question", id: id, "data-testid": `qanda-block__question-${id}`, "data-trackable": "liveqa_question", children: [subtitle && (_jsx("div", { className: "o3-type-headline-sm qanda-block__question-subtitle", children: subtitle })), _jsx("div", { className: "o3-type-body-highlight qanda-block__comment-byline", children: _jsx(Byline, { structuredContent: byline, showEditedBy: false }) }), _jsx("div", { className: "o3-type-body-base qanda-block__comment-text", children: _jsx(Expander, { className: "qanda-block__text", expandLabel: "Expand question", collapseLabel: "Collapse question", id: id, children: _jsx(RichText, { structuredContent: body.structured }) }) })] }, id), children?.map((child) => child.body && (_jsxs("div", { className: "qanda-block__comment qanda-block__answer", "data-testid": `qanda-block__answer-${child.id}`, "data-trackable": "liveqa_answer", children: [_jsxs("div", { className: "qanda-block__answer-header", children: [_jsx("div", { className: "o3-type-body-highlight qanda-block__comment-byline", children: child.author ? (_jsx(Author, { byline: child.byline, headshot: child.author.headshot, prefLabel: child.author.prefLabel, role: child.author.role, streamPage: child.author.streamPage })) : (_jsx(Byline, { structuredContent: child.byline, showEditedBy: false })) }), showAnswerTime && (_jsx("span", { className: "o3-type-detail", children: renderTime(child.publishedDate) }))] }), _jsx("div", { className: "o3-type-body-base qanda-block__comment-text", children: _jsx(Expander, { className: "qanda-block__text", expandLabel: "Expand answer", collapseLabel: "Collapse answer", id: id, children: _jsx(RichText, { structuredContent: child.body.structured }) }) })] }, child.id)))] }));
|
|
12
70
|
}
|
|
13
71
|
export default QandaBlock;
|
|
@@ -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
|
|
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' && (
|
|
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 ?
|
|
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
|
}
|