*/
const ReviewerBadge = ({ badgeText, shouldUseAlternateColorToken = false }) => {
const theme = useTheme();
const badgeColor = shouldUseAlternateColorToken
? getColorToken(theme, 'colors.interactive.social.primary')
: getColorToken(theme, 'colors.background.brand');
return (React.createElement(ReviewerBadgeWrapper, { "data-testid": "reviewer-badge" },
React.createElement(VerificationBadgeIcon, { color: badgeColor }),
React.createElement(ReviewListBadgeText, { shouldUseAlternateColorToken: shouldUseAlternateColorToken }, badgeText)));
};
ReviewerBadge.propTypes = {
badgeText: PropTypes.string,
shouldUseAlternateColorToken: PropTypes.bool
};
ReviewerBadge.displayName = 'ReviewerBadge';
module.exports = asConfiguredComponent(ReviewerBadge, 'ReviewerBadge');
//# sourceMappingURL=ReviewerBadge.js.map
/***/ }),
/***/ 50787:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
module.exports = __webpack_require__(71549);
//# sourceMappingURL=index.js.map
/***/ }),
/***/ 55917:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const styled = (__webpack_require__(92168)["default"]);
const { BaseText, BaseWrap } = __webpack_require__(76955);
const { calculateSpacing, getColorStyles, getTypographyStyles } = __webpack_require__(26865);
const { VerificationBadge } = __webpack_require__(24695);
const ReviewerBadgeWrapper = styled(BaseWrap).withConfig({
displayName: 'ReviewerBadgeWrapper'
}) `
display: flex;
`;
const ReviewListBadgeText = styled(BaseText).withConfig({
displayName: 'ReviewBadgeText'
}) `
padding-left: ${calculateSpacing()};
${getTypographyStyles('typography.definitions.foundation.meta-secondary')}
${({ shouldUseAlternateColorToken }) => shouldUseAlternateColorToken
? getColorStyles('color', 'colors.interactive.social.primary')
: getColorStyles('color', 'colors.background.brand')};
`;
const VerificationBadgeIcon = styled(VerificationBadge).withConfig({
displayName: 'VerificationBadgeIcon'
}) ``;
module.exports = {
ReviewerBadgeWrapper,
ReviewListBadgeText,
VerificationBadgeIcon
};
//# sourceMappingURL=styles.js.map
/***/ }),
/***/ 5697:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { getFromNowDateFormat } = __webpack_require__(45526);
module.exports = {
getFromNowDateFormat
};
//# sourceMappingURL=index.js.map
/***/ }),
/***/ 45526:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const translations = __webpack_require__(39965);
const DEFAULT_LOCALE = 'en-US';
/**
* Get the publish date for live story
*
* @param {string} locale - optional: language to localise the date into.
* @param {string} date - Publish Date
* @param {any} formatMessage - Required for translations
* @param {boolean} [includeHourAndMin] - Optional for including the hour and minutes
*
* @returns {string} - the formatted published timestamp
*/
const getFromNowDateFormat = ({ locale = DEFAULT_LOCALE, date, formatMessage, includeHourAndMin = false }) => {
const rtf = new Intl.RelativeTimeFormat(locale, {
localeMatcher: 'best fit',
numeric: 'always',
style: 'long'
});
const rtfTimeFormat = new Intl.DateTimeFormat(locale, {
hour: '2-digit',
minute: '2-digit',
hour12: true,
localeMatcher: 'best fit'
});
const MS_SECOND = 1000;
const MS_MINUTE = MS_SECOND * 60;
const MS_HOUR = MS_MINUTE * 60;
const MS_DAY = MS_HOUR * 24;
const MS_MONTH = MS_DAY * 30;
const MS_YEAR = MS_DAY * 365;
const FROM_NOW_JUST_NOW = MS_SECOND * 44;
const FROM_NOW_MINUTE = MS_SECOND * 89;
const FROM_NOW_MINUTES = MS_MINUTE * 44;
const FROM_NOW_HOUR = MS_MINUTE * 89;
const FROM_NOW_HOURS = MS_HOUR * 21;
const FROM_NOW_DAY = MS_HOUR * 35;
const FROM_NOW_DAYS = MS_DAY * 25;
const FROM_NOW_MONTH = MS_DAY * 45;
const FROM_NOW_MONTHS = MS_DAY * 319;
const FROM_NOW_YEAR = MS_DAY * 547;
const fromNow = (value) => {
const nowTick = new Date().getTime();
const valueTick = new Date(value).getTime();
const delta = nowTick - valueTick;
if (delta <= FROM_NOW_JUST_NOW) {
return formatMessage(translations.fewSecondsAgoLabel);
}
else if (delta <= FROM_NOW_MINUTE) {
return formatMessage(translations.aMinAgoLabel);
}
else if (delta <= FROM_NOW_MINUTES) {
return rtf.format(-Math.ceil(delta / MS_MINUTE), 'minute');
}
else if (delta <= FROM_NOW_HOUR) {
return formatMessage(translations.anHourAgoLabel);
}
else if (delta <= FROM_NOW_HOURS) {
return rtf.format(-Math.ceil(delta / MS_HOUR), 'hour');
}
else if (delta <= FROM_NOW_DAY) {
return formatMessage(translations.aDayAgoLabel);
}
else if (delta <= FROM_NOW_DAYS) {
return rtf.format(-Math.ceil(delta / MS_DAY), 'day');
}
else if (delta <= FROM_NOW_MONTH) {
return formatMessage(translations.aMonthAgoLabel);
}
else if (delta <= FROM_NOW_MONTHS) {
return rtf.format(-Math.ceil(delta / MS_MONTH), 'month');
}
else if (delta <= FROM_NOW_YEAR) {
return formatMessage(translations.aYearAgoLabel);
}
return rtf.format(-Math.ceil(delta / MS_YEAR), 'year');
};
const getTheTimeFormat = (dateTime) => {
if (dateTime) {
const timeFromNow = fromNow(dateTime);
const hourAndMin = rtfTimeFormat.format(new Date(dateTime));
if (includeHourAndMin)
return `${timeFromNow}, ${hourAndMin}`;
return timeFromNow;
}
return null;
};
return getTheTimeFormat(date);
};
module.exports = { getFromNowDateFormat };
//# sourceMappingURL=publishDate.js.map
/***/ }),
/***/ 39965:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { defineMessages } = __webpack_require__(46984);
module.exports = defineMessages({
fewSecondsAgoLabel: {
id: 'LiveStory.feedFewSecondsAgoLabel',
defaultMessage: 'a few seconds ago',
description: ''
},
aMinAgoLabel: {
id: 'LiveStory.feedAMinAgoLabel',
defaultMessage: 'a minute ago',
description: ''
},
anHourAgoLabel: {
id: 'LiveStory.feedAnHourAgoLabel',
defaultMessage: 'an hour ago',
description: ''
},
aDayAgoLabel: {
id: 'LiveStory.feedADayAgoLabel',
defaultMessage: 'a day ago',
description: ''
},
aMonthAgoLabel: {
id: 'LiveStory.feedAMonthAgoLabel',
defaultMessage: 'a month ago',
description: ''
},
aYearAgoLabel: {
id: 'LiveStory.feedAYearAgoLabel',
defaultMessage: 'a year ago',
description: ''
}
});
//# sourceMappingURL=translations.js.map
/***/ }),
/***/ 3890:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const React = __webpack_require__(96540);
const PropTypes = __webpack_require__(5556);
const { useIntl } = __webpack_require__(46984);
const { useEffect, useRef, useState } = __webpack_require__(96540);
const { TrackComponentChannel } = __webpack_require__(78788);
const { trackTagEvent } = __webpack_require__(55144);
const InformationIcon = __webpack_require__(25965);
const ToggleChip = __webpack_require__(15343);
const ToggleChipList = __webpack_require__(99244);
const translations = (__webpack_require__(39321)/* ["default"] */ .A);
const userNameModalActions = __webpack_require__(61057);
const ReviewNoteModal = __webpack_require__(45771);
const { trackUserAccountEvent } = __webpack_require__(14307);
const ImageUpload = __webpack_require__(72667);
const { AlertArrow, ReviewNoteFormWrapper, ReviewNoteSectionContainer, ReviewNoteUserInfo, ReviewerInfoLabel, ReviewerName, ReviewerInfoIconButtonWrapper, ReviewNoteRatingWrapper, RatingFormRating, ReviewNotesFormMinimised, ReviewerRatingLabel, ReviewerInfoAlertToolTip, ReveiwerInfoText, ReviewTagsInfoLabel, ReviewNotesToggleChipListWrapper, ReviewNotesFormActions, ReviewNotesFormCancelButton, ReviewNotesFormTextFieldErrorText, ReviewNotesFormSubmitButton, ReviewNoteTextField, ReviewNotesDivider, ReviewNotesImageUploadWrapper } = __webpack_require__(37887);
const TextField = __webpack_require__(89662);
const Button = __webpack_require__(73730);
const { trackContentEngagementEvent } = __webpack_require__(14307);
const { useOutsideClick } = __webpack_require__(87098);
const MinimisedNotesFormWrapper = ({ ariaLabel, children, onMinimise, usernameSignInDek, trackAddNoteEvent, handleUsernameChange, siteUserName }) => {
const eventData = {
type: 'impression',
label: 'Create Username',
subject: 'username_modal',
source_type: 'community_comment'
};
const usernameCheck = (e) => {
if (siteUserName) {
onMinimise(e, siteUserName);
}
else if (siteUserName !== undefined) {
trackUserAccountEvent(eventData);
userNameModalActions.doDisplayModal({
dangerousDek: usernameSignInDek,
successCallback: (result) => {
handleUsernameChange(result);
onMinimise(e, result);
},
source: 'community_comment'
});
}
};
return (React.createElement(ReviewNotesFormMinimised, { role: "button", tabIndex: "0", onClick: (e) => {
trackAddNoteEvent();
usernameCheck(e);
}, onKeyPress: onMinimise, "aria-label": ariaLabel }, children));
};
MinimisedNotesFormWrapper.propTypes = {
ariaLabel: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
handleUsernameChange: PropTypes.func.isRequired,
onMinimise: PropTypes.func.isRequired,
siteUserName: PropTypes.string,
trackAddNoteEvent: PropTypes.func,
usernameSignInDek: PropTypes.string
};
/**
* ReviewNotesForm component
*
* @param {object} props - React props
* @param {string} [props.addNoteLabel] - Optional label when adding to notes
* @param {string} [props.addNoteFailedToastMessage] - Optional message for failure to add comment
* @param {string} [props.className] - Optional top-level class to add
* @param {Function} [props.handleUsernameChange] - Function to set user name
* @param {boolean} [props.shouldEnableCommentsImageUpload] - flag to enable image upload in comments
* @param {bool} [props.isMinimised] - The initial view of the form is minimised
* @param {string} [props.minimisedReviewNotesText] - The text on the initial fake input field
* @param {string} [props.nonLoggedInErrorMessage] - Optional error message when not logged in
* @param {string} [props.reviewerInfoFieldLabel] - Optional label for info field
* @param {string} [props.reviewTagsLabel] - Optional label for tags
* @param {object} [props.modalProps] - Optional props to show labels of Modal
* @param {Function} [props.onSubmitHandler] - Function to call on submit
* @param {string} [props.reviewerInfoText] - Optional text for reviewer info alert icon.
* @param {string} [props.reviewerRatingLabel] - rating section title
* @param {Array} [props.reviewNoteTags] - Optional prop to configure the review note toggle chips
* @param {bool} [props.shouldEnableRatings] - flag to enable community rating
* @param {bool} [props.shouldEnableTags] - flag to enable tags in comments
* @param {boolean} [props.shouldUseInteractiveBrandColor] - Optional prop to override the color of the toggle chips
* @param {bool} [props.shouldUseFullOpacity] - Optional flag to increase opacity if brands backrgound.brand token is light
* @param {func} [props.showMessageBannerHandler] - Optional function to showtoastMessage on success or failure
* @param {bool} [props.showTextFieldBoxShadow] - Optional to add box-shadow property
* @param {bool} [props.showTextFieldRoundedEdges] - Optional to add border radius property
* @param {Function} [props.showSavedRecipeNotes] - Function to set the state for saved recipe notes
* @param {string} [props.signInURL] - Required URL for users to sign in
* @param {string} [props.siteUserName] - Commenting user name
* @param {string} [props.userId] - Required user id to be included in form submission
* @param {string} [props.usernameSignInDek] - Optional prop to use a custom username modal dek
* @param {object} [props.validations] - Checks for the form
* @param {object} [props.validations.errorMessage] - Custom error messages
* @param {number} [props.validations.max] - maxim number of characters for a comment
* @param {number} [props.validations.min] - minimum number of characters for a comment
* @param {number} [props.validations.remainingChar] - remaining character number at which to warn
* @returns {ReactElement}
*/
const ReviewNotesForm = ({ addNoteFailedToastMessage, addNoteLabel, className, shouldEnableTags = false, reviewNoteTags = [], reviewerRatingLabel, shouldUseInteractiveBrandColor = false, validations: { min = 5, max = 3000, remainingChar = 100, errorMessage = {} } = {}, usernameSignInDek, modalProps = {}, minimisedReviewNotesText, nonLoggedInErrorMessage, isMinimised = true, reviewerInfoText, signInURL, userId, handleUsernameChange, onSubmitHandler, reviewerInfoFieldLabel, reviewTagsLabel, siteUserName, showSavedRecipeNotes, shouldEnableCommentsImageUpload, shouldEnableRatings, showMessageBannerHandler, shouldUseFullOpacity, showTextFieldBoxShadow = false, showTextFieldRoundedEdges = false }) => {
React.useEffect(() => {
window.Kendra.TRACK_COMPONENT.broadcast(TrackComponentChannel.RENDER, {
name: 'ReviewNotesForm'
});
}, []);
const formConfig = {
reviewNote: '',
toggleChip: [],
...(shouldEnableRatings && { rating: null })
};
const intl = useIntl();
const [formData, setFormData] = useState(formConfig);
const [errors, setErrors] = useState({});
const [warningMessage, setWarningMessage] = useState('');
const [shouldHideReviewerInfoAlert, setShouldHideReviewerInfoAlert] = useState(true);
const [tags, setTags] = useState(reviewNoteTags);
const [isReviewNoteModalOpen, setIsReviewNoteModalOpen] = useState(false);
const [isImageUploading, setIsImageUploading] = useState(false);
const validationRules = {
reviewNote: [
{
test: (value) => value.length >= min && value.length <= max,
error: 'invalidReviewLength'
}
],
rating: [
{
test: (value) => value >= 1,
error: 'requiredField'
}
]
};
const [minimised, setMinimised] = React.useState(isMinimised);
const [isSignedIn, setIsSignedIn] = React.useState(false);
const [signInError, setSignInError] = React.useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
setIsSignedIn(!!userId);
}, [userId]);
const reviewNotesTextPlaceHolder = minimisedReviewNotesText ||
intl.formatMessage(translations.defaultcommunityReviewText);
const updateTagsStatus = (tagSlug) => {
const updatedTags = tags.map((tag) => tag.slug === tagSlug ? { ...tag, active: !tag.active } : tag);
setTags((prev) => {
trackTagEvent(updatedTags, prev);
return updatedTags;
});
return updatedTags;
};
const updateWarningMessage = (value) => {
if (value.length >= max) {
setWarningMessage(intl.formatMessage(translations.maxCharLimitMet).replace('_MAX_', max));
}
else if (max - value.length <= remainingChar) {
setWarningMessage(intl
.formatMessage(translations.remainingMaxCharLimit)
.replace('_COUNT_', max - value.length)
.replace('_MAX_', max));
}
else {
setWarningMessage('');
}
};
const validateField = (field, value) => {
const rules = validationRules[field];
if (!rules)
return null;
for (const rule of rules) {
if (!rule.test(value)) {
setErrors((prevErrors) => ({
...prevErrors,
[field]: rule.error
}));
return rule.error;
}
}
setErrors((prevErrors) => {
const { [field]: removedError, ...restErrors } = prevErrors;
return restErrors;
});
return null;
};
const validateFormOnSubmit = (formData) => {
return Object.entries(formData).reduce((errors, [field, value]) => {
const error = validateField(field, value);
if (error) {
errors[field] = error;
}
return errors;
}, {});
};
const handleOnChange = (field) => (value) => {
let newValue;
switch (field) {
case 'reviewNote':
newValue = value.target.value.trim();
updateWarningMessage(newValue);
break;
case 'toggleChip':
newValue = updateTagsStatus(value);
break;
default:
newValue = value;
break;
}
setFormData({
...formData,
[field]: newValue
});
// don't trigger validation when user start typing for first time
if (field === 'reviewNote' && newValue.length < min) {
return;
}
validateField(field, newValue);
};
const trackAddNoteEvent = () => {
if (true) {
const eventData = {
type: 'attempt',
subject: 'community_comment'
};
trackContentEngagementEvent(eventData, { skipDuplicateEvent: false });
}
};
const ref = useRef(null);
const signInRef = useRef(null);
const reviewNoteRef = React.useRef(null);
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setShouldHideReviewerInfoAlert(true);
}
if (signInRef.current &&
signInRef.current.id !== event.target?.firstChild.id) {
setSignInError(false);
}
};
const showForm = (e, userName) => {
e.preventDefault();
userName && setMinimised(false);
showSavedRecipeNotes(false);
};
useOutsideClick(ref, (e) => handleClickOutside(e));
useOutsideClick(signInRef, (e) => handleClickOutside(e));
const toggle = (e) => {
e.preventDefault();
setShouldHideReviewerInfoAlert(!shouldHideReviewerInfoAlert);
};
const handleCancelClick = () => {
const shouldShowModal = Object.values(formData).some((item) => {
if (Array.isArray(item) && item.length === 0) {
return false;
}
return Boolean(item);
});
if (shouldShowModal) {
setIsReviewNoteModalOpen(true);
}
else {
setMinimised(true);
setErrors({});
}
};
const toggleChips = () => {
return tags.map(({ slug, active, description }) => (React.createElement(ToggleChip, { key: slug, isChecked: active, onChange: () => handleOnChange('toggleChip')(slug), shouldUrlRedirect: false, isDisabled: isSubmitting }, intl.formatMessage(translations.reviewTags, {
reviewTag: description
}))));
};
const { discardLabel = 'Yes, discard it' } = modalProps;
const trackDiscardNoteEvent = () => {
if (true) {
const eventData = {
type: 'discard',
label: discardLabel.toUpperCase(),
subject: 'community_comment'
};
trackContentEngagementEvent(eventData, { skipDuplicateEvent: false });
}
};
const handleClearForm = () => {
reviewNoteRef.current.value = '';
setTags(reviewNoteTags);
setFormData({ ...formConfig });
setErrors({});
setMinimised(true);
setIsReviewNoteModalOpen(false);
setWarningMessage('');
};
const handleSubmit = async () => {
setIsSubmitting(true);
const newErrors = validateFormOnSubmit(formData);
if (Object.keys(newErrors).length === 0) {
const data = { ...formData, userId };
const resp = await onSubmitHandler(data);
if (resp != null) {
handleClearForm();
showSavedRecipeNotes(true);
}
else {
showMessageBannerHandler &&
showMessageBannerHandler(addNoteFailedToastMessage ||
intl.formatMessage(translations.AddNoteFailedToastMessage));
}
}
setIsSubmitting(false);
};
const handleDiscardNote = () => {
handleClearForm();
trackDiscardNoteEvent();
};
const getErrorMessage = (errors, name) => {
const errorItem = errors[name];
if (!errorItem)
return '';
const customErrorMessage = errorMessage[errorItem];
if (customErrorMessage)
return customErrorMessage.replace('_MIN_', min);
return intl.formatMessage(translations[errorItem], {
min
});
};
const invalidReview = getErrorMessage(errors, 'reviewNote');
const hasWarning = warningMessage.length > 0;
const handleImageUpload = (response) => {
if (response?.data?.[0]?.filePath) {
setFormData((prevData) => ({
...prevData,
images: [
{
id: response?.data?.[0]?.filePath,
url: response?.data?.[0]?.encodedURI
}
]
}));
}
else {
console.error('Error uploading image:', response);
}
};
const handleImageUploadStatusChange = (isUploading) => {
setIsImageUploading(isUploading);
};
const productName = 'commenting';
return (React.createElement(ReviewNoteFormWrapper, { className: className, "data-testid": "ReviewNotesForm" }, isSignedIn &&
(minimised ? (React.createElement(MinimisedNotesFormWrapper, { ariaLabel: reviewNotesTextPlaceHolder, onMinimise: showForm, signInURL: signInURL, isSignedIn: isSignedIn, trackAddNoteEvent: trackAddNoteEvent, handleUsernameChange: handleUsernameChange, siteUserName: siteUserName, usernameSignInDek: usernameSignInDek },
React.createElement(ReviewNoteTextField, { name: "isMinimised", formName: "isMinimised", label: intl.formatMessage(translations.textFieldLabel), placeholder: reviewNotesTextPlaceHolder, hasDynamicTextArea: true, hasBoxShadow: showTextFieldBoxShadow, hasRoundedEdges: showTextFieldRoundedEdges, tabIndex: "-1", "aria-hidden": true, customHeightMultiplier: 12, hideLabel: true, isInvalid: signInError, inputRef: signInRef, isDisabled: signInError }),
signInError && (React.createElement(ReviewNotesFormTextFieldErrorText, null, nonLoggedInErrorMessage ||
intl.formatMessage(translations.nonLoggedInErrorMessage))))) : (React.createElement(ReviewNoteSectionContainer, { hasError: !!invalidReview, hasWarning: hasWarning },
React.createElement(TextField.MultiLine, { name: "reviewNoteText", placeholder: reviewNotesTextPlaceHolder, hideLabel: true, label: intl.formatMessage(translations.textFieldLabel), formName: "reviewNoteText", hasAutoFocus: true, hasBoxShadow: showTextFieldBoxShadow, hasRoundedEdges: showTextFieldRoundedEdges, inputRef: reviewNoteRef, errorText: invalidReview, onInputChange: handleOnChange('reviewNote'), max: max, errorPosition: "belowTextField", shouldDisableTypingAtMaxChar: true, isDisabled: isSubmitting }),
hasWarning && (React.createElement(ReviewNotesFormTextFieldErrorText, null, warningMessage)),
shouldEnableCommentsImageUpload && (React.createElement(ReviewNotesImageUploadWrapper, null,
React.createElement(ImageUpload, { onFileChange: handleImageUpload, onUploadStatusChange: handleImageUploadStatusChange, id: "review-note-image-upload", product: productName }))),
shouldEnableTags && (React.createElement(React.Fragment, null,
React.createElement(ReviewTagsInfoLabel, null, reviewTagsLabel ||
intl.formatMessage(translations.reviewTagsLabel)),
React.createElement(ReviewNotesToggleChipListWrapper, { shouldUseInteractiveBrandColor: shouldUseInteractiveBrandColor, shouldUseFullOpacity: shouldUseFullOpacity },
React.createElement(ToggleChipList, null, toggleChips())))),
shouldEnableRatings && (React.createElement(ReviewNoteRatingWrapper, null,
React.createElement(ReviewerRatingLabel, null, reviewerRatingLabel ??
intl.formatMessage(translations.reviewerRatingLabel)),
React.createElement(RatingFormRating, { averageRatingCount: formData.rating, isRatingDisabled: false, onChange: handleOnChange('rating'), shouldShowOutline: false }),
Object.keys(errors).length > 0 && (React.createElement(ReviewNotesFormTextFieldErrorText, null, getErrorMessage(errors, 'rating'))))),
React.createElement(ReviewNoteUserInfo, null,
React.createElement(ReviewerInfoLabel, null, reviewerInfoFieldLabel ||
intl.formatMessage(translations.reviewerInfoFieldLabel)),
React.createElement(ReviewerName, null, siteUserName),
React.createElement(ReviewerInfoIconButtonWrapper, { ref: ref },
React.createElement(Button.Utility, { isIconButton: true, ButtonIcon: InformationIcon, className: "review-note-user__info-button", onClickHandler: (e) => toggle(e), inputKind: "button", role: "button", label: intl.formatMessage(translations.reviewerInfoIconButtonLabel) }),
!shouldHideReviewerInfoAlert && (React.createElement(ReviewerInfoAlertToolTip, null,
React.createElement(AlertArrow, null),
React.createElement(ReveiwerInfoText, null, reviewerInfoText ??
intl.formatMessage(translations.reviewerFieldInfoIconText))))),
React.createElement(ReviewNotesDivider, null)),
React.createElement(ReviewNoteModal, { confirmButtonCallback: handleDiscardNote, modalProps: modalProps, onClose: () => setIsReviewNoteModalOpen(false), isVisible: isReviewNoteModalOpen }),
React.createElement(ReviewNotesFormActions, { "data-testid": "ReviewNotesFormActions" },
React.createElement(ReviewNotesFormSubmitButton, { isDisabled: isSubmitting ||
isImageUploading ||
Object.keys(errors).length > 0, inputKind: "button", label: addNoteLabel || intl.formatMessage(translations.addNoteLabel), onClickHandler: handleSubmit }),
React.createElement(ReviewNotesFormCancelButton, { isDisabled: isSubmitting, btnStyle: "text", inputKind: "link", type: "button", label: intl.formatMessage(translations.cancelNoteLabel), onClickHandler: handleCancelClick })))))));
};
ReviewNotesForm.propTypes = {
addNoteFailedToastMessage: PropTypes.string,
addNoteLabel: PropTypes.string,
className: PropTypes.string,
handleUsernameChange: PropTypes.func,
isMinimised: PropTypes.bool,
minimisedReviewNotesText: PropTypes.string,
modalProps: PropTypes.object,
nonLoggedInErrorMessage: PropTypes.string,
onSubmitHandler: PropTypes.func,
reviewerInfoFieldLabel: PropTypes.string,
reviewerInfoText: PropTypes.string,
reviewerRatingLabel: PropTypes.string,
reviewNoteTags: PropTypes.array,
reviewTagsLabel: PropTypes.string,
shouldEnableCommentsImageUpload: PropTypes.bool,
shouldEnableRatings: PropTypes.bool,
shouldEnableTags: PropTypes.bool,
shouldUseFullOpacity: PropTypes.bool,
shouldUseInteractiveBrandColor: PropTypes.bool,
showMessageBannerHandler: PropTypes.func,
showSavedRecipeNotes: PropTypes.func.isRequired,
showTextFieldBoxShadow: PropTypes.bool,
showTextFieldRoundedEdges: PropTypes.bool,
signInURL: PropTypes.string.isRequired,
siteUserName: PropTypes.string,
userId: PropTypes.string,
usernameSignInDek: PropTypes.string,
validations: PropTypes.shape({
min: PropTypes.number,
max: PropTypes.number,
remainingChar: PropTypes.number,
errorMessage: PropTypes.shape({
requiredField: PropTypes.string,
invalidReviewLength: PropTypes.string
})
})
};
module.exports = ReviewNotesForm;
//# sourceMappingURL=ReviewNotesForm.js.map
/***/ }),
/***/ 45565:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { asConfiguredComponent } = __webpack_require__(12892);
const ReviewNotesForm = __webpack_require__(3890);
module.exports = asConfiguredComponent(ReviewNotesForm, 'ReviewNotesForm');
//# sourceMappingURL=index.js.map
/***/ }),
/***/ 37887:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { css, default: styled } = __webpack_require__(92168);
const { BaseText, BaseLink } = __webpack_require__(76955);
const { calculateSpacing, getColorStyles, getColorToken, getTypographyStyles, maxScreen, minScreen } = __webpack_require__(26865);
const { BREAKPOINTS } = __webpack_require__(96472);
const Rating = __webpack_require__(62340);
const { RatingStar } = __webpack_require__(97927);
const { TextFieldControlTextarea, TextFieldWrapper } = __webpack_require__(60434);
const TextField = __webpack_require__(89662);
const { ListWrapper } = __webpack_require__(14952);
const { ToggleButton } = __webpack_require__(18161);
const Button = __webpack_require__(73730);
const { TextFieldErrorText } = __webpack_require__(60434);
const { ImageUploadWrapper } = __webpack_require__(44741);
const ReviewNoteTextField = styled(TextField.MultiLine).withConfig({
displayName: 'ReviewNoteTextField'
}) `
margin-bottom: 0;
textarea:disabled {
background-color: transparent;
}
`;
const ReviewNoteFormWrapper = styled.div.withConfig({
displayName: 'ReviewNoteFormWrapper'
}) ``;
const ReviewNoteSectionContainer = styled.div.withConfig({
displayName: 'ReviewNoteSectionContainer'
}) `
border: 1px solid;
${({ theme }) => getColorStyles(theme, 'border-color', 'colors.interactive.base.light')};
padding: ${calculateSpacing(3)};
${TextFieldControlTextarea} {
margin-top: 0;
padding: ${calculateSpacing(2)} ${calculateSpacing(1.5)};
${({ theme, hasError }) => hasError
? getColorStyles(theme, 'border-color', 'colors.interactive.base.brand-secondary')
: getColorStyles(theme, 'border-color', 'colors.interactive.base.black')};
&[disabled] {
${({ theme }) => getColorStyles(theme, 'background', 'colors.interactive.base.white')};
${({ theme }) => getColorStyles(theme, 'border-color', 'colors.interactive.base.light')};
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.subhed')};
}
&::placeholder {
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.light')};
}
}
${TextFieldWrapper} {
${({ hasWarning }) => hasWarning && `margin-bottom: ${calculateSpacing(1)};`}
}
`;
const ReviewNoteUserInfo = styled.div.withConfig({
displayName: 'ReviewNoteUserInfo'
}) `
margin-top: ${calculateSpacing(4)};
margin-bottom: ${calculateSpacing(4)};
`;
const ReviewNoteRatingWrapper = styled.div.withConfig({
displayName: 'ReviewNoteRatingWrapper'
}) `
margin-top: ${calculateSpacing(1)};
margin-bottom: ${calculateSpacing(4)};
`;
const ReviewerInfoLabel = styled.span.withConfig({
displayName: 'ReviewerInfoLabel'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.body-deemphasized')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-core')};
`;
const ReviewerRatingLabel = styled.span.withConfig({
displayName: 'ReviewerRatingLabel'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.body')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-core')};
`;
const ReviewerName = styled.span.withConfig({
displayName: 'ReviewerName'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.body')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-core')};
padding-left: ${calculateSpacing(0.5)};
`;
const ReviewTagsInfoLabel = styled.span.withConfig({
displayName: 'ReviewTagsInfoLabel'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.body')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-core')};
`;
const ReviewerInfoIconButtonWrapper = styled.div.withConfig({
displayName: 'ReviewerInfoIconButtonWrapper'
}) `
display: inline;
svg {
width: 24px;
height: 24px;
${({ theme }) => getColorStyles(theme, 'fill', 'colors.consumption.lead.special.context-tertiary')};
}
.review-note-user__info-button {
float: inline-end;
margin: 0;
border: 0;
background-color: ${({ theme }) => getColorToken(theme, 'colors.interactive.base.white')};
padding: 0;
padding-left: 12px;
&:hover,
&:focus {
border: 0;
background: none;
}
}
`;
const ReviewNotesToggleChipListWrapper = styled.div.withConfig({
displayName: 'ReviewNotesToggleChipListWrapper'
}) `
${ToggleButton} {
${getTypographyStyles('typography.definitions.foundation.link-utility')}
&[aria-checked='false'] {
${({ shouldUseInteractiveBrandColor, shouldUseFullOpacity }) => {
if (shouldUseInteractiveBrandColor) {
const opacity = shouldUseFullOpacity ? 1 : 0.1;
return css `
background-color: rgba(
${getColorToken('colors.interactive.social.primary-hover', {
rgbOnly: true
})},
${opacity}
);
`;
}
return `${getColorStyles('color', 'colors.interactive.base.black')}`;
}}
}
&:focus {
box-shadow: none;
}
&:hover {
box-shadow: 0 0 0 1px ${getColorToken('colors.interactive.base.black')}
inset;
}
${maxScreen(BREAKPOINTS.md)} {
&:focus,
&:hover {
box-shadow: none;
}
}
&:disabled {
box-shadow: none;
}
}
${ListWrapper} {
padding-bottom: 8px;
padding-left: 0;
}
`;
const ReveiwerInfoText = styled(BaseText).withConfig({
displayName: 'ReveiwerInfoText'
}) `
position: absolute;
left: calc(25% - 10px);
float: inline-end;
z-index: -1;
border-radius: 8px;
box-shadow: 0 0 20px 12px rgba(0, 0, 0, 0.1);
background: white;
padding: 17px 22px;
width: 80%;
box-sizing: border-box;
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.dark')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.utility.input-core')};
${minScreen(BREAKPOINTS.sm)} {
padding: 12px 13px;
}
${minScreen(BREAKPOINTS.md)} {
padding: 17px 22px;
}
`;
const AlertArrow = styled.div.withConfig({
displayName: 'AlertArrow'
}) `
position: absolute;
top: auto;
bottom: 100%;
left: calc(98% - 11px);
border-width: 0 10px 13px;
border-style: solid;
border-color: rgb(254 254 254) transparent;
`;
const ReviewerInfoAlertToolTip = styled.div.withConfig({
displayName: 'ReviewerInfoAlertToolTip'
}) `
position: relative;
z-index: 2;
margin-top: 8px;
background-color: ${getColorToken('colors.background.white')};
${minScreen(BREAKPOINTS.sm)} {
${ReveiwerInfoText} {
left: calc(8% - 4px);
width: 100%;
}
${AlertArrow} {
left: calc(98% - 14px);
border-width: 0 8px 12px;
}
}
${minScreen(BREAKPOINTS.md)} {
${ReveiwerInfoText} {
left: calc(25% - 10px);
width: 80%;
}
${AlertArrow} {
left: calc(98% - 12px);
border-width: 0 10px 13px;
}
}
${minScreen(BREAKPOINTS.lg)} {
${ReveiwerInfoText} {
left: calc(7% - 10px);
width: 100%;
}
${AlertArrow} {
left: calc(98% - 14px);
border-width: 0 10px 13px;
}
}
${minScreen(BREAKPOINTS.xl)} {
${ReveiwerInfoText} {
left: calc(25% - 10px);
width: 80%;
}
${AlertArrow} {
left: calc(98% - 11px);
border-width: 0 10px 13px;
}
}
${minScreen(BREAKPOINTS.xxl)} {
${ReveiwerInfoText} {
left: calc(34% - 10px);
width: 70%;
}
${AlertArrow} {
left: calc(98% - 8px);
border-width: 0 10px 13px;
}
}
`;
const RatingFormRating = styled(Rating).withConfig({
displayName: 'RatingFormRating'
}) `
align-items: start;
padding: ${calculateSpacing()} 0 ${calculateSpacing()};
${RatingStar} {
transform: scale(1.78);
margin: 0 ${calculateSpacing(1.25)};
}
`;
const ReviewNotesFormActions = styled.div.withConfig({
displayName: 'ReviewNotesFormActions'
}) `
${minScreen(BREAKPOINTS.lg)} {
display: grid;
grid-template-columns: repeat(2, auto);
gap: 32px;
}
${maxScreen(BREAKPOINTS.md)} {
display: flex;
flex-direction: column;
}
`;
const ReviewNotesDivider = styled.div.withConfig({
displayName: 'ReviewNotesDivider'
}) `
margin-top: ${calculateSpacing(2)};
border-bottom: 1px solid;
${getColorStyles('border-color', 'colors.consumption.body.standard.divider')};
`;
const ReviewNotesFormCancelButton = styled(Button.Primary).withConfig({
displayName: 'ReviewFormSubmitButton'
}) `
${getTypographyStyles('typography.definitions.utility.button-core')}
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.brand-primary')};
margin-top: 0.5rem;
margin-bottom: 20px;
padding: 15px 9px;
width: 100%;
max-width: 100%;
text-decoration: underline;
`;
const ReviewNotesFormSubmitButton = styled(Button.Primary).withConfig({
displayName: 'ReviewFormSubmitButton'
}) `
display: block;
margin-top: 0.5rem;
margin-bottom: 20px;
padding: 15px 9px;
width: 100%;
max-width: 100%;
height: unset;
text-align: center;
&:active::before {
top: 0;
left: 0;
}
`;
const ReviewNotesFormTextFieldErrorText = styled(TextFieldErrorText).withConfig({
displayName: 'ReviewFormTextFieldErrorText'
}) ``;
const ReviewNotesFormSignin = styled(BaseLink).withConfig({
displayName: 'ReviewNotesFormSignin'
}) ``;
const ReviewNotesFormMinimised = styled.div.withConfig({
displayName: 'ReviewNotesFormMinimised'
}) `
position: relative;
input.text-field__control {
cursor: pointer;
}
@media (max-width: 768px) {
textarea.text-field__control {
padding: ${calculateSpacing(2)};
}
}
@media (min-width: 768px) {
textarea.text-field__control {
padding: ${calculateSpacing(2)} ${calculateSpacing(3)};
}
}
textarea.text-field__control {
cursor: pointer;
overflow: auto;
overflow-y: hidden;
&::placeholder {
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.light')};
}
}
${ReviewNotesFormSignin} {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
color: transparent;
}
`;
const ReviewNotesImageUploadWrapper = styled.div.withConfig({
displayName: 'ReviewNotesImageUploadWrapper'
}) `
${ImageUploadWrapper} {
${maxScreen(BREAKPOINTS.lg)} {
margin-top: ${calculateSpacing(2)};
margin-bottom: ${calculateSpacing(0.5)};
}
${minScreen(BREAKPOINTS.lg)} {
margin-top: ${calculateSpacing(3)};
margin-bottom: ${calculateSpacing(1.25)};
}
}
`;
module.exports = {
AlertArrow,
ReviewNoteFormWrapper,
ReviewNoteSectionContainer,
ReviewNoteUserInfo,
ReviewerInfoLabel,
ReviewerName,
ReviewerInfoIconButtonWrapper,
ReviewNoteRatingWrapper,
RatingFormRating,
ReviewerRatingLabel,
ReviewNotesFormSignin,
ReviewNotesFormMinimised,
ReviewerInfoAlertToolTip,
ReveiwerInfoText,
ReviewTagsInfoLabel,
ReviewNotesToggleChipListWrapper,
ReviewNotesFormActions,
ReviewNotesFormCancelButton,
ReviewNotesFormSubmitButton,
ReviewNotesFormTextFieldErrorText,
ReviewNoteTextField,
ReviewNotesDivider,
ReviewNotesImageUploadWrapper
};
//# sourceMappingURL=styles.js.map
/***/ }),
/***/ 55144:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.trackTagEvent = void 0;
const snowplow_tracking_1 = __webpack_require__(14307);
const trackTagEvent = (updatedTags, prevTags) => {
const activeTags = updatedTags.filter((tag) => tag.active);
if (activeTags.length === 0) {
return;
}
// Check if any tag changed from inactive to active
const inactiveToActiveTags = prevTags.some((tag, i) => !tag.active && updatedTags[i].active);
if (!inactiveToActiveTags) {
return;
}
const label = activeTags.length === 1 ? activeTags[0].description : '';
const features_list = activeTags.map(({ label }) => ({
name: label.toLowerCase(),
index: 0,
total_index: 1
}));
(0, snowplow_tracking_1.trackContentEngagementEvent)({
type: 'select',
label,
subject: 'community_comment',
features_list
}, { skipDuplicateEvent: false });
};
exports.trackTagEvent = trackTagEvent;
//# sourceMappingURL=tracking.js.map
/***/ }),
/***/ 39321:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
var __webpack_unused_export__;
__webpack_unused_export__ = ({ value: true });
const react_intl_1 = __webpack_require__(46984);
exports.A = (0, react_intl_1.defineMessages)({
defaultcommunityReviewText: {
id: 'ReviewNotesForm.defaultcommunityReviewText',
defaultMessage: 'Ask a question or leave a helpful tip, suggestion or opinion that is relevant and respectful for the community.',
description: 'Appears when the form is minimised or maximised and enableCommunityExperience is true'
},
nonLoggedInErrorMessage: {
id: 'ReviewForm.nonLoggedInErrorMessage',
defaultMessage: 'Sign in or create an account to add comment.',
description: 'Message to display non logged in users'
},
textFieldLabel: {
id: 'ReviewNotesForm.textFieldLabel',
defaultMessage: 'Your Review',
description: 'The label for the main review text field'
},
addNoteLabel: {
id: 'ReviewNotesForm.addNoteLabel',
defaultMessage: 'Add comment',
description: 'The label for Add Comment submit button'
},
cancelNoteLabel: {
id: 'ReviewNotesForm.cancelNoteLabel',
defaultMessage: 'Discard',
description: 'The label for cancel button'
},
reviewerInfoFieldLabel: {
id: 'ReviewNotesForm.ReviewerInfoFieldLabel',
defaultMessage: 'Commenting as:',
description: 'The label for the reviewer name field'
},
reviewerRatingLabel: {
id: 'ReviewNotesForm.ReviewerRatingLabel',
defaultMessage: 'Rate this',
description: 'The label for the reviewer rating field'
},
reviewerFieldInfoIconText: {
id: 'ReviewNotesForm.reviewerFieldInfoIconText',
defaultMessage: 'Your username appears next to your comments and replies. Change it anytime in your Account.',
description: 'information text for user to change their user name'
},
reviewerInfoIconButtonLabel: {
id: 'ReviewNotesForm.reviewerInfoIconButtonLabel',
defaultMessage: 'user name update message',
description: 'Label for reviewer user name update message icon'
},
reviewTagsLabel: {
id: 'ReviewNotesForm.reviewTagsLabel',
defaultMessage: 'TAG YOUR COMMENT (OPTIONAL)',
description: 'Label for adding tags'
},
reviewTags: {
id: 'FilterComponent.reviewTags',
defaultMessage: '{reviewTag}',
description: 'Value for the tag'
},
invalidReviewLength: {
id: 'ReviewNotesForm.invalidReviewNoteLength',
defaultMessage: 'Enter {min} characters or more to add a comment.',
description: 'Error message for invalid review comment length'
},
requiredField: {
id: 'ReviewNotesForm.requiredField',
defaultMessage: 'Select a star rating to add a comment',
description: 'Error message for required field'
},
maxCharLimitMet: {
id: 'ReviewNotesForm.maxCharLimitMet',
defaultMessage: '_MAX_ character limit met',
description: 'Warning message for max review comment length'
},
remainingMaxCharLimit: {
id: 'ReviewNotesForm.remainingMaxCharLimit',
defaultMessage: '_COUNT_ of _MAX_ character limit remaining',
description: 'Warning message for invalid review comment length'
},
buttonLabel: {
id: 'ReviewNotesForm.buttonLabel',
defaultMessage: 'Sign in or create account',
description: 'Text for the sign in or create account button'
},
AddNoteFailedToastMessage: {
id: 'ReviewNotes.AddNoteFailedToastMessage',
defaultMessage: 'Unable to add your comment. Please try again.',
description: 'Failure message to show on comment save'
}
});
//# sourceMappingURL=translations.js.map
/***/ }),
/***/ 32844:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ReviewReplyComment = void 0;
const prop_types_1 = __importDefault(__webpack_require__(5556));
const react_1 = __importStar(__webpack_require__(96540));
const react_intl_1 = __webpack_require__(46984);
const cookies_1 = __importDefault(__webpack_require__(53788));
const utils_1 = __webpack_require__(60711);
const user_authentication_1 = __webpack_require__(20656);
const ReviewListContainer_1 = __webpack_require__(24300);
const utils_2 = __webpack_require__(85554);
const SignInModalActions_1 = __webpack_require__(22509);
const UserNameModalActions_1 = __webpack_require__(61057);
const snowplow_tracking_1 = __webpack_require__(14307);
const utils_3 = __webpack_require__(39311);
const thin_1 = __webpack_require__(91470);
const thinner_1 = __webpack_require__(24695);
const styles_1 = __webpack_require__(16631);
const ReviewReplyNote_1 = __importDefault(__webpack_require__(71001));
const styles_2 = __webpack_require__(10959);
const reviewer_badge_1 = __importDefault(__webpack_require__(50787));
const tracking_1 = __webpack_require__(75454);
const { oidcCookies } = cookies_1.default;
const translations_1 = __importDefault(__webpack_require__(20777));
const utils_js_1 = __webpack_require__(26576);
const formatReplyNotes = (replies, formatMessage, username) => {
if (!replies)
return [];
return replies.map((reply) => {
const { id, author: { orgRole: role } = {}, body: replyText, createdAt, siteUsername: siteUserObject, parent, actionCounts, viewerActionPresence, revision, story, images = [] } = reply;
const parentCommentUserName = () => {
let name = username;
if (username === 'Anonymous') {
name = username;
}
else if (parent?.siteUsername?.[0]?.name) {
name = parent?.siteUsername?.[0]?.name;
}
return name;
};
return {
role,
replyId: id,
replyText,
replyDate: (0, utils_3.formatReviewDateAgo)(createdAt, formatMessage),
replyAuthorName: siteUserObject?.[0]?.name,
parentAuthorName: parentCommentUserName(),
reactionCount: actionCounts?.reaction?.total || 0,
viewerActionPresence,
revisionId: revision?.id,
storyURL: story?.url,
images
};
});
};
/**
*
* ReviewReplyComment component
* @param {object} props - ReviewReplyComment props
* @param {Array} [props.replies] - Replies to a comment displayed as reply list
* @param {string} [props.username] - Username which indicates the reviewer username
* @param {number} [props.replyPageInfo] - PageInfo to help in pagination of replies
* @param {string} [props.repliesOrderBy] - Value to set replies ordering
* @param {string} [props.reviewModalProps] - Modal props to handle discard flow in reply note component
* @param {string} [props.reviewerBadges] - Array of role and badge for the reviewer badge
* @param {string} [props.usernameSignInDek] - Optional prop to use a custom username modal dek
* @param {string} [props.signInHed] - Header on signin alert shown to user
* @param {string} [props.signInHedSpanTag] - Hed span tag on signin alert shown to user
* @param {string} [props.signInMessage] - Message on signin alert shown to user
* @param {string} [props.shouldEnableReply] - Flag to enable replying on comments
* @param {object} [props.user] - User information
* @param {string} [props.commentId] - Comment id of the primary comment to which replies are fetched
* @param {number} [props.replyLimit] - number of replies to show per request when clicked on show more replies label
* @param {Function} [props.updateUserReactions] - function to update state values of load more replies in ReviewListContainer component
* @param {Function} [props.commentReactionHandler] - function to handle to like action on replies
* @param {string} [props.contentId] - content id
* @param {Function} [props.showMessageBannerHandler] - handler function to show Message Banner
* @param {object} [props.userReactions] - user reactions object
* @param {string} [props.siteUserName] - site user name
* @param {string} [props.handleUsernameChange] - function to handle user name change
* @param {bool} [props.shouldEnableUpvotes] - flag to enable community likes or upvotes
* @param {string} [props.commentingUrl] - URL for the coral API where comments are stored
* @param {bool} [props.shouldUseAlternateColorToken] - Optional flag to change font color if token is light
* @param {bool} [props.isFeatured] - Flag indicating a featured item
* @param {bool} [props.shouldEnableRepliesImageUpload] - Flag to enable image upload functionality on replies
* @param {bool} [props.spectraUrl] - Spectra url for image display
* @param {string} [props.upvoteIcon] - Icon name for upvote button
* @param {string} [props.upvoteIconFilled] - Icon name for filled upvote button
* @param {bool} [props.showUpvoteLabel] - flag to show upvote label text
*
* @returns {ReactElement}
*/
const ReviewReplyComment = ({ commentingUrl, contentId, replies: defaultReplies, replyPageInfo, repliesOrderBy, username, reviewerBadges, reviewModalProps, commentReactionHandler, user = {}, signInHed, usernameSignInDek, signInHedSpanTag, signInMessage, commentId, replyLimit, shouldUseAlternateColorToken, showMessageBannerHandler, userReactions, updateUserReactions, siteUserName, handleUsernameChange, shouldEnableReply, shouldEnableUpvotes, isFeatured, spectraUrl, shouldEnableRepliesImageUpload, upvoteIcon, upvoteIconFilled, showUpvoteLabel }) => {
const { formatMessage } = (0, react_intl_1.useIntl)();
const [replyUserName, setReplyUsername] = (0, react_1.useState)(null);
const [replyOpen, setReplyOpen] = (0, react_1.useState)(false);
const [replyIDs, setReplyIDs] = (0, react_1.useState)([]);
const [displayHideRepliesLabel, setDisplayHideRepliesLabel] = (0, react_1.useState)(false);
// Use the utility function to get icon components based on icon name
const UpvoteIcon = (upvoteIcon && (0, utils_js_1.getIconComponent)(upvoteIcon)) || thinner_1.Like;
const UpvoteIconFilled = (upvoteIconFilled && (0, utils_js_1.getIconComponent)(upvoteIconFilled)) || thinner_1.LikeFilled;
const [displayShowRepliesLabel, setDisplayShowRepliesLabel] = (0, react_1.useState)(replyPageInfo.hasNextPage ?? false);
const [repliesLabel, setRepliesLabel] = (0, react_1.useState)(replyPageInfo.hasNextPage
? formatMessage(translations_1.default.ShowMoreRepliesLabel)
: '');
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
const [hideReplies, setHideReplies] = (0, react_1.useState)(false);
const [endCursor, setEndCursor] = (0, react_1.useState)(replyPageInfo.endCursor);
const [hasMoreReplies, setHasMoreReplies] = (0, react_1.useState)(replyPageInfo.hasNextPage ?? false);
const [nextReplies, setNextReplies] = (0, react_1.useState)([]);
const COMMUNITY_REPLY = 'community_reply';
const handleReplyEvent = (e, reply) => {
e.preventDefault();
if (!user.isAuthenticated) {
const source = 'COMMUNITY_REPLY_TO_REPLY';
const eventData = {
subject: COMMUNITY_REPLY,
label: 'Reply',
source_type: source,
type: 'login',
...(0, tracking_1.getPlacementData)(isFeatured)
};
const featured = (0, tracking_1.getFeaturedQueryParam)(isFeatured);
const redirectURL = (0, utils_2.getRelativeURLWithSearchAndHash)({
href: window.location.href,
hash: utils_2.commentingAction.REPLY_TO_REPLY,
queryParams: { featured, source }
});
(0, SignInModalActions_1.doDisplayModal)({
dangerousDek: signInMessage,
dangerousHed: signInHed,
dangerousHedSpanTag: signInHedSpanTag,
redirectURL,
shouldHideIllustration: true,
source,
snowplowData: eventData
});
}
else if (siteUserName) {
setReplyUsername(reply?.replyAuthorName);
setReplyIDs((prevReplyIDs) => [...prevReplyIDs, reply?.replyId]);
setReplyOpen(true);
}
else {
(0, UserNameModalActions_1.doDisplayModal)({
dangerousDek: usernameSignInDek,
successCallback: (result) => {
handleUsernameChange(result);
setReplyUsername(reply?.replyAuthorName);
setReplyIDs((prevReplyIDs) => [...prevReplyIDs, reply?.replyId]);
setReplyOpen(true);
},
source: COMMUNITY_REPLY,
isFeatured
});
const eventData = {
type: 'impression',
subject: 'username_modal',
label: 'Create Username',
source_type: COMMUNITY_REPLY,
...(0, tracking_1.getPlacementData)(isFeatured)
};
(0, snowplow_tracking_1.trackUserAccountEvent)(eventData);
}
const eventData = {
type: 'attempt',
subject: COMMUNITY_REPLY,
label: 'reply',
items: [
{
content_id: reply?.replyId
}
],
...(0, tracking_1.getPlacementData)(isFeatured)
};
(0, snowplow_tracking_1.trackContentEngagementEvent)(eventData, { skipDuplicateEvent: false });
};
const replyLikeActionHandler = (item) => {
if (!user.isAuthenticated) {
const source = 'COMMUNITY_LIKE_CLICK_REPLY';
const eventData = {
type: 'login',
source_type: source,
...(0, tracking_1.getPlacementData)(isFeatured)
};
const likeEventData = {
type: 'like',
subject: COMMUNITY_REPLY,
...(0, tracking_1.getPlacementData)(isFeatured)
};
const featured = (0, tracking_1.getFeaturedQueryParam)(isFeatured);
(0, snowplow_tracking_1.trackContentEngagementEvent)(likeEventData, { skipDuplicateEvent: false });
const queryParams = {
action: ReviewListContainer_1.CREATE_COMMENT_ACTION,
commentId: item.commentId,
commentRevisionId: item.revisionId,
featured,
source
};
const redirectURL = (0, utils_2.getRelativeURLWithSearchAndHash)({
href: window.location.href,
hashValue: utils_2.commentingAction.LIKE_REPLY,
queryParams
});
(0, SignInModalActions_1.doDisplayModal)({
dangerousDek: signInMessage,
dangerousHed: signInHed,
dangerousHedSpanTag: signInHedSpanTag,
redirectURL,
shouldHideIllustration: true,
snowplowData: eventData,
source
});
return;
}
commentReactionHandler({
item
});
const type = userReactions[item.commentId]?.viewerActionPresence
? 'unlike'
: 'like';
const eventData = {
type,
subject: COMMUNITY_REPLY,
items: [
{
content_id: item.commentId
}
],
...(0, tracking_1.getPlacementData)(isFeatured)
};
(0, snowplow_tracking_1.trackContentEngagementEvent)(eventData, { skipDuplicateEvent: false });
};
const loadMoreReplies = async () => {
let errorMessage;
setHideReplies(false);
if (nextReplies.length >= 1 && !hasMoreReplies) {
setNextReplies([...nextReplies]);
setDisplayHideRepliesLabel(true);
setDisplayShowRepliesLabel(false);
}
else {
setIsLoading(true);
const nextCursor = endCursor ?? replyPageInfo.endCursor;
try {
const accessToken = user_authentication_1.UserAuthenticationClient.getCookieValue(oidcCookies.id);
const { replies, page: { endCursor: newCursor, hasNextPage } } = await (0, utils_1.getRepliesByCommentId)({
commentId,
after: nextCursor,
commentingUrl,
logger: console,
accessToken,
replyLimit,
repliesOrderBy
});
setEndCursor(newCursor);
setHasMoreReplies(hasNextPage);
if (!hasNextPage) {
setDisplayHideRepliesLabel(true);
}
setDisplayShowRepliesLabel(hasNextPage);
setNextReplies([...nextReplies, ...replies]);
updateUserReactions(replies);
}
catch (error) {
errorMessage = error?.message || '';
console.warn(error);
}
setIsLoading(false);
}
const entityData = {
type: 'show_more',
label: 'Show more replies',
subject: COMMUNITY_REPLY,
error: errorMessage,
...(0, tracking_1.getPlacementData)(isFeatured)
};
(0, snowplow_tracking_1.trackContentEngagementEvent)(entityData, { skipDuplicateEvent: false });
};
const hideAllReplies = () => {
setDisplayHideRepliesLabel(false);
setHideReplies(true);
setDisplayShowRepliesLabel(true);
};
(0, react_1.useEffect)(() => {
if (isLoading) {
setRepliesLabel(formatMessage(translations_1.default.LoadingRepliesLabel));
}
else if (displayShowRepliesLabel) {
setRepliesLabel(formatMessage(translations_1.default.ShowMoreRepliesLabel));
}
}, [isLoading, displayShowRepliesLabel, formatMessage]);
const replyNotes = (replies) => {
const newReplies = formatReplyNotes(replies, formatMessage, username);
return newReplies.map((reply) => {
const { role, replyId, replyAuthorName, replyDate, replyText, parentAuthorName, revisionId, storyURL, images } = reply || {};
const item = {
commentId: replyId,
revisionId
};
const { badge: badgeText } = reviewerBadges?.find((item) => item.role === role) || {};
return (react_1.default.createElement(styles_2.ReplyCommentItem, { key: replyId },
react_1.default.createElement(styles_1.ReviewMetaGrid, null, replyAuthorName && (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(styles_2.ReplierUserName, null, replyAuthorName),
badgeText && (react_1.default.createElement(reviewer_badge_1.default, { badgeText: badgeText, shouldUseAlternateColorToken: shouldUseAlternateColorToken }))))),
react_1.default.createElement(styles_2.ReplyDataInfo, null,
react_1.default.createElement(styles_2.ReplyMetaData, null,
react_1.default.createElement(styles_2.ReplyInfoLabel, null, formatMessage(translations_1.default.ReviewReplyLabel)),
parentAuthorName && (react_1.default.createElement(styles_2.ReplyUserName, null, parentAuthorName)),
react_1.default.createElement(thin_1.Dot, null),
replyDate && react_1.default.createElement(styles_2.ReplyTimeStamp, null, replyDate))),
replyText && (react_1.default.createElement(styles_2.ReplyText, { dangerouslySetInnerHTML: { __html: replyText } })),
shouldEnableRepliesImageUpload &&
images.map((image, index) => (react_1.default.createElement(styles_2.ReplyImage, { key: index, src: (0, utils_js_1.getPreviewUrl)(image?.url, spectraUrl) }))),
react_1.default.createElement(styles_2.ReplyDataInfo, null,
shouldEnableUpvotes && (react_1.default.createElement(styles_2.ReplyReactionButton, { isIconButton: true, name: "reply-reaction", label: "Reaction", onClickHandler: () => replyLikeActionHandler(item), ButtonIcon: userReactions[replyId]?.viewerActionPresence
? UpvoteIconFilled
: UpvoteIcon })),
shouldEnableUpvotes && (react_1.default.createElement(styles_2.ReplyLikeCount, null, (userReactions[replyId]?.reactionCount &&
userReactions[replyId]?.reactionCount) ||
(showUpvoteLabel && formatMessage(translations_1.default.upvoteLabel)))),
shouldEnableReply && (react_1.default.createElement(styles_1.ReviewReplyWrapper, { onClick: (e) => handleReplyEvent(e, reply) },
react_1.default.createElement(thinner_1.Comment, null),
react_1.default.createElement(styles_1.ReviewReplyLabel, null, formatMessage(translations_1.default.ReviewReplyCommentLabel))))),
replyOpen &&
replyIDs.includes(replyId) &&
user.isAuthenticated &&
siteUserName && (react_1.default.createElement(ReviewReplyNote_1.default, { commentId: replyId, username: replyUserName, contentId: contentId, revisionId: revisionId, commentingUrl: commentingUrl, closeReply: () => {
setReplyOpen(false);
}, reviewModalProps: reviewModalProps, showMessageBannerHandler: showMessageBannerHandler, source: "community_reply", storyURL: storyURL, isFeatured: isFeatured, shouldEnableRepliesImageUpload: shouldEnableRepliesImageUpload }))));
});
};
return (react_1.default.createElement(styles_2.ReplyCommentsListWrapper, null,
replyNotes(defaultReplies),
!hideReplies && replyNotes(nextReplies),
react_1.default.createElement(styles_2.ReplyDataInfo, null,
displayShowRepliesLabel && (react_1.default.createElement(styles_2.ShowOrHideRepliesLabel, { onClick: loadMoreReplies },
react_1.default.createElement(styles_2.ShowOrHideRepliesLabelRule, null),
repliesLabel)),
displayHideRepliesLabel && (react_1.default.createElement(styles_2.ShowOrHideRepliesLabel, { onClick: hideAllReplies },
react_1.default.createElement(styles_2.ShowOrHideRepliesLabelRule, null),
formatMessage(translations_1.default.HideRepliesLabel))))));
};
exports.ReviewReplyComment = ReviewReplyComment;
exports.ReviewReplyComment.propTypes = {
commentId: prop_types_1.default.string,
commentingUrl: prop_types_1.default.string.isRequired,
commentReactionHandler: prop_types_1.default.func,
contentId: prop_types_1.default.string,
handleUsernameChange: prop_types_1.default.func,
isFeatured: prop_types_1.default.bool,
replies: prop_types_1.default.array,
repliesOrderBy: prop_types_1.default.string,
replyLimit: prop_types_1.default.number,
replyPageInfo: prop_types_1.default.object,
reviewerBadges: prop_types_1.default.arrayOf(prop_types_1.default.shape({
role: prop_types_1.default.string,
badge: prop_types_1.default.string
})),
reviewModalProps: prop_types_1.default.object,
shouldEnableRepliesImageUpload: prop_types_1.default.bool,
shouldEnableReply: prop_types_1.default.bool,
shouldEnableUpvotes: prop_types_1.default.bool,
shouldUseAlternateColorToken: prop_types_1.default.bool,
showMessageBannerHandler: prop_types_1.default.func,
showUpvoteLabel: prop_types_1.default.bool,
signInHed: prop_types_1.default.string,
signInHedSpanTag: prop_types_1.default.string,
signInMessage: prop_types_1.default.string,
siteUserName: prop_types_1.default.string,
spectraUrl: prop_types_1.default.string,
updateUserReactions: prop_types_1.default.func,
upvoteIcon: prop_types_1.default.string,
upvoteIconFilled: prop_types_1.default.string,
user: prop_types_1.default.shape({
isAuthenticated: prop_types_1.default.bool.isRequired,
amguuid: prop_types_1.default.string
}).isRequired,
username: prop_types_1.default.string,
usernameSignInDek: prop_types_1.default.string,
userReactions: prop_types_1.default.shape({
reactionCount: prop_types_1.default.number.isRequired,
viewerActionPresence: prop_types_1.default.bool
}).isRequired
};
//# sourceMappingURL=ReviewReplyComment.js.map
/***/ }),
/***/ 10959:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const styled = (__webpack_require__(92168)["default"]);
const { getColorStyles, getTypographyStyles, minScreen, calculateSpacing } = __webpack_require__(26865);
const { ReviewListMetaInfo, ReviewerUserName, ReviewLikeCount, ReviewListTimeStamp, ReviewReplyLabel, ReviewText, ReviewListReactionButton, ReviewImage } = __webpack_require__(16631);
const { BREAKPOINTS } = __webpack_require__(96472);
const { BaseText } = __webpack_require__(76955);
const ReplyUserName = styled(BaseText).withConfig({
displayName: 'ReplyUserName'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.dark')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-feature')};
`;
const ReplyDataInfo = styled(ReviewListMetaInfo).withConfig({
displayName: 'ReplyDataInfo'
}) `
&:first-child {
margin-bottom: ${calculateSpacing(1)};
}
.icon-p4k-upvote,
.icon-p4k-upvote-filled {
margin-right: ${calculateSpacing(0.5)};
cursor: pointer;
width: auto;
height: auto;
&:hover path[fill='#757575'] {
${({ theme }) => getColorStyles(theme, 'fill', 'colors.interactive.base.brand-secondary')};
}
}
`;
const ReplierUserName = styled(ReviewerUserName).withConfig({
displayName: 'ReplierUserName'
}) ``;
const ReplyMetaData = styled.div.withConfig({
displayName: 'ReplyMetaData'
}) `
display: flex;
flex-direction: row;
align-items: center;
`;
const ReplyCommentsListWrapper = styled.div.withConfig({
displayName: 'ReplyCommentsListWrapper'
}) `
margin-top: ${calculateSpacing(1.25)};
margin-left: ${calculateSpacing(6)};
${minScreen(BREAKPOINTS.md)} {
margin-left: ${calculateSpacing(8)};
}
`;
const ReplyCommentItem = styled.div.withConfig({
displayName: 'ReplyCommentItem'
}) `
margin-top: ${calculateSpacing(4)};
&:first-child {
margin-top: ${calculateSpacing(2)};
}
`;
const ReplyLikeCount = styled(ReviewLikeCount).withConfig({
displayName: 'ReplyLikeCount'
}) ``;
const ReplyTimeStamp = styled(ReviewListTimeStamp).withConfig({
displayName: 'ReplyTimeStamp'
}) `
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.foundation.meta-secondary')}
`;
const ReplyInfoLabel = styled(ReviewReplyLabel).withConfig({
displayName: 'ReplyInfoLabel'
}) `
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.foundation.meta-secondary')};
`;
const ReplyText = styled(ReviewText).withConfig({
displayName: 'ReplyText'
}) ``;
const ReplyImage = styled(ReviewImage).withConfig({
displayName: 'ReplyImage'
}) `
margin-top: ${calculateSpacing(1)};
`;
const ReplyReactionButton = styled(ReviewListReactionButton).withConfig({
displayName: 'ReplyReactionButton'
}) ``;
const ShowOrHideRepliesLabel = styled.div.withConfig({
displayName: 'ShowOrHideRepliesLabel'
}) `
display: flex;
flex-direction: row;
cursor: pointer;
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.foundation.link-secondary')};
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.dark')};
`;
const ShowOrHideRepliesLabelRule = styled.hr.withConfig({
displayName: 'ShowOrHideRepliesLabelRule'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.discovery.body.dark.divider')};
align-self: center;
margin-right: ${calculateSpacing(1)};
width: ${calculateSpacing(4)};
`;
module.exports = {
ReplyUserName,
ReplyDataInfo,
ReplyCommentsListWrapper,
ReplyCommentItem,
ReplyMetaData,
ReplierUserName,
ReplyLikeCount,
ReplyTimeStamp,
ReplyInfoLabel,
ReplyText,
ReplyImage,
ReplyReactionButton,
ShowOrHideRepliesLabel,
ShowOrHideRepliesLabelRule
};
//# sourceMappingURL=styles.js.map
/***/ }),
/***/ 20777:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
const react_intl_1 = __webpack_require__(46984);
exports["default"] = (0, react_intl_1.defineMessages)({
ReviewReplyLabel: {
id: 'ReviewReplyComment.ReviewReplyLabel',
defaultMessage: 'Replying to',
description: 'The label for the reply comment field'
},
ShowMoreRepliesLabel: {
id: 'ReviewReplyComment.ShowMoreRepliesLabel',
defaultMessage: 'Show more replies',
description: 'The label to show more replies'
},
ReviewReplyCommentLabel: {
id: 'ReviewReplyComment.ReviewReplyCommentLabel',
defaultMessage: 'Reply',
description: 'The label to show reply icon'
},
HideRepliesLabel: {
id: 'ReviewReplyComment.HideRepliesLabel',
defaultMessage: 'Hide replies',
description: 'The label to hide replies'
},
LoadingRepliesLabel: {
id: 'ReviewReplyComment.LoadingRepliesLabel',
defaultMessage: 'Loading…',
description: 'The label to hide replies'
},
upvoteLabel: {
id: 'ReviewReplyComment.upvoteLabel',
defaultMessage: 'Upvote',
description: 'Label for upvote button'
}
});
//# sourceMappingURL=translations.js.map
/***/ }),
/***/ 71001:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const React = __webpack_require__(96540);
const PropTypes = __webpack_require__(5556);
const { useState, useRef } = __webpack_require__(96540);
const { useIntl } = __webpack_require__(46984);
const translations = (__webpack_require__(76833)/* ["default"] */ .A);
const { addReply, modifyStory } = __webpack_require__(60711);
const { getPlacementData } = __webpack_require__(75454);
const { ReviewReplyUsername, ReviewReplyNoteWrapper, ReviewReplyLabel, ReviewReplyCancelLink, ReviewReplyNoteInfo, ReviewReplyButton, ReviewReplyButtonWrapper, ReviewReplyMultilineTextField, ReviewReplyImageUploadWrapper } = __webpack_require__(73559);
const ReviewNoteModal = __webpack_require__(45771);
const ImageUpload = __webpack_require__(72667);
const { trackContentEngagementEvent } = __webpack_require__(14307);
const CHARACTER_LIMIT = 3000;
const ALERT_THRESHOLD = 2900;
const MIN_REPLY_LENGTH = 2;
const ReviewReplyNote = ({ commentId, contentId, contentTitle = '', username, revisionId, commentingUrl, closeReply, reviewModalProps, showMessageBannerHandler, storyURL, source, isFeatured, shouldEnableRepliesImageUpload }) => {
const { formatMessage } = useIntl();
const [isDisabled, setIsDisabled] = useState(true);
const [charCount, setCharCount] = useState(0);
const [isMinLengthError, setMinLengthError] = useState(false);
const [isImageUploading, setIsImageUploading] = useState(false);
const [uploadedImage, setUploadedImage] = useState(null);
const replyRef = useRef(null);
const [isReviewNoteModalOpen, setIsReviewNoteModalOpen] = useState(false);
const discardReplyNote = (event) => {
const eventData = {
type: 'discard',
label: 'YES, DISCARD IT',
subject: source,
...getPlacementData(isFeatured)
};
event.preventDefault();
closeReply();
trackContentEngagementEvent(eventData, { skipDuplicateEvent: false });
};
const handleReplyTextChange = (e) => {
const replyComments = e.target.value;
setCharCount(replyComments.length);
e.target.value = replyComments;
setIsDisabled(replyComments.trim().length === 0);
setMinLengthError(false);
};
const handleCancelClick = (e) => {
if (replyRef.current && replyRef.current.value.trim().length > 0) {
setIsReviewNoteModalOpen(true);
}
else {
discardReplyNote(e);
}
replyRef.current.focus();
};
const handleImageUpload = (response) => {
if (response?.data?.[0]?.filePath) {
setUploadedImage({
id: response.data[0].filePath,
url: response.data[0].encodedURI
});
}
else {
console.error('Error uploading image:', response);
}
};
const handleImageUploadStatusChange = (isUploading) => {
setIsImageUploading(isUploading);
};
const handleReplySubmit = async (e) => {
e.preventDefault();
let error_info;
const replyText = replyRef.current?.value?.trim();
if (replyText.length < MIN_REPLY_LENGTH) {
setMinLengthError(true);
setIsDisabled(true);
}
else if (replyText.length > 0) {
const input = {
storyID: contentId,
parentID: commentId,
parentRevisionID: revisionId,
body: replyText,
...(uploadedImage && { images: [uploadedImage] }),
clientMutationId: '0'
};
try {
// ff the url of the story is already feeded, its fine but if not we need to add the the url
// for all new Comments, url will be passed, and story will always exist because reply is on a comment button
// for all the old comments where story doesnt have the url, if we add the reply directoryUrl, we will need to update url
// this code covers that scenario
if (!storyURL) {
await modifyStory({
id: contentId,
title: contentTitle,
url: window.location.origin + window.location.pathname,
commentingUrl,
logger: console
});
}
const isReplySuccessFull = await addReply(commentingUrl, input);
if (isReplySuccessFull) {
closeReply();
showMessageBannerHandler(formatMessage(translations.AddReplySuccessToastMessage));
}
else {
showMessageBannerHandler(formatMessage(translations.AddReplyFailedToastMessage));
}
}
catch (err) {
error_info = err?.message || '';
console.error('Error while posting reply:', err);
showMessageBannerHandler(formatMessage(translations.AddReplyFailedToastMessage));
}
const eventData = {
type: 'submit',
label: 'Reply',
subject: 'community_reply',
error: error_info,
...getPlacementData(isFeatured)
};
trackContentEngagementEvent(eventData, { skipDuplicateEvent: false });
}
};
const getErrorText = () => {
if (charCount > ALERT_THRESHOLD && charCount < CHARACTER_LIMIT) {
return formatMessage(translations.ReviewFieldAlertLimitErrorText)
.replace('_CHARACTER_LIMIT_CURRENT_', CHARACTER_LIMIT - charCount)
.replace('_CHARACTER_LIMIT_', CHARACTER_LIMIT);
}
if (charCount === CHARACTER_LIMIT) {
return formatMessage(translations.ReviewFieldMaxLimitErrorText).replace('_CHARACTER_LIMIT_', CHARACTER_LIMIT);
}
return '';
};
const errorMessageHandler = () => {
if (isMinLengthError) {
return formatMessage(translations.ReviewFieldMinLimitErrorText);
}
return getErrorText();
};
const handleCharCount = (count) => {
setCharCount(count);
};
const productName = 'commenting';
return (React.createElement(ReviewReplyNoteWrapper, null,
React.createElement(ReviewReplyNoteInfo, null,
React.createElement(ReviewReplyLabel, null, formatMessage(translations.ReviewReplyLabel)),
React.createElement(ReviewReplyUsername, null, username)),
React.createElement(ReviewReplyMultilineTextField, { name: "reviewReplyNoteText", formName: "reviewReplyNoteText", placeholder: formatMessage(translations.ReplyFieldPlaceHolder), hasAutoFocus: true, onInputChange: handleReplyTextChange, customHeightMultiplier: 10, label: formatMessage(translations.ReplyTextFieldLabel), hideLabel: true, errorPosition: "belowTextField", errorText: errorMessageHandler(), inputRef: replyRef, max: CHARACTER_LIMIT, charCountHandler: handleCharCount, shouldDisableTypingAtMaxChar: true }),
shouldEnableRepliesImageUpload && (React.createElement(ReviewReplyImageUploadWrapper, null,
React.createElement(ImageUpload, { onFileChange: handleImageUpload, onUploadStatusChange: handleImageUploadStatusChange, id: "review-reply-image-upload", product: productName }))),
React.createElement(ReviewNoteModal, { modalProps: reviewModalProps, confirmButtonCallback: (e) => discardReplyNote(e), onClose: () => setIsReviewNoteModalOpen(false), isVisible: isReviewNoteModalOpen }),
React.createElement(ReviewReplyButtonWrapper, null,
React.createElement(ReviewReplyButton, { inputKind: "submit", isDisabled: isDisabled || isImageUploading, label: formatMessage(translations.ReplyButtonLabel), onClickHandler: handleReplySubmit }),
React.createElement(ReviewReplyCancelLink, { btnStyle: "text", label: formatMessage(translations.CancelButtonLabel), onClickHandler: handleCancelClick }))));
};
ReviewReplyNote.propTypes = {
closeReply: PropTypes.func,
commentId: PropTypes.string,
commentingUrl: PropTypes.string,
contentId: PropTypes.string,
contentTitle: PropTypes.string,
isFeatured: PropTypes.bool,
reviewModalProps: PropTypes.object,
revisionId: PropTypes.string,
shouldEnableRepliesImageUpload: PropTypes.bool,
showMessageBannerHandler: PropTypes.func,
source: PropTypes.string,
storyURL: PropTypes.string,
username: PropTypes.string
};
module.exports = ReviewReplyNote;
//# sourceMappingURL=ReviewReplyNote.js.map
/***/ }),
/***/ 73559:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const styled = (__webpack_require__(92168)["default"]);
const { getColorStyles, getColorToken, getTypographyStyles, maxScreen, minScreen, calculateSpacing } = __webpack_require__(26865);
const Button = __webpack_require__(73730);
const { BaseText } = __webpack_require__(76955);
const { BREAKPOINTS } = __webpack_require__(96472);
const TextField = __webpack_require__(89662);
const { ImageUploadWrapper } = __webpack_require__(44741);
const ReviewReplyUsername = styled(BaseText).withConfig({
displayName: 'ReviewReplyUsername'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.body')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-core')};
padding-left: ${calculateSpacing(0.5)};
`;
const ReviewReplyNoteWrapper = styled.div.withConfig({
displayName: 'ReviewReplyNoteWrapper'
}) `
border: 1px solid ${getColorToken('colors.foundation.menu.dividers')};
padding: ${calculateSpacing(3)};
gap: ${calculateSpacing(1.5)};
${maxScreen(BREAKPOINTS.md)} {
margin-top: ${calculateSpacing(1.25)};
}
`;
const ReviewReplyCancelLink = styled(Button.Primary).withConfig({
displayName: 'ReviewReplyCancelLink'
}) `
margin-top: ${calculateSpacing(1.25)};
width: 100%;
${getTypographyStyles('typography.definitions.utility.button-core')}
text-decoration: underline;
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.brand-primary')};
`;
const ReviewReplyMultilineTextField = styled(TextField.MultiLine).withConfig({
displayName: 'ReviewReplyMultilineTextField'
}) `
${getTypographyStyles('typography.definitions.utility.input-core')}
${({ theme }) => getColorStyles(theme, 'color', 'colors.interactive.base.light')};
`;
const ReviewReplyLabel = styled.span.withConfig({
displayName: 'ReviewReplyLabel'
}) `
${({ theme }) => getColorStyles(theme, 'color', 'colors.consumption.body.standard.body-deemphasized')};
${({ theme }) => getTypographyStyles(theme, 'typography.definitions.globalEditorial.accreditation-core')};
`;
const ReviewReplyNoteInfo = styled.div.withConfig({
displayName: 'ReviewReplyNoteInfo'
}) `
display: flex;
`;
const ReviewReplyButton = styled(Button.Primary).withConfig({
displayName: 'ReviewReplyButton'
}) `
display: block;
margin-top: 0.5rem;
margin-bottom: 20px;
padding: 15px 9px;
width: 100%;
max-width: 100%;
height: unset;
text-align: center;
&:active::before {
top: 0;
left: 0;
}
`;
const ReviewReplyButtonWrapper = styled.div.withConfig({
displayName: 'ReviewReplyButtonWrapper'
}) `
${minScreen(BREAKPOINTS.lg)} {
display: grid;
grid-template-columns: repeat(2, auto);
gap: 32px;
}
${maxScreen(BREAKPOINTS.md)} {
display: flex;
flex-direction: column;
}
`;
const ReviewReplyImageUploadWrapper = styled.div.withConfig({
displayName: 'ReviewReplyImageUploadWrapper'
}) `
${ImageUploadWrapper} {
margin-top: ${calculateSpacing(2)};
margin-bottom: ${calculateSpacing(1)};
${minScreen(BREAKPOINTS.lg)} {
margin-top: ${calculateSpacing(3)};
margin-bottom: ${calculateSpacing(2)};
}
}
`;
module.exports = {
ReviewReplyUsername,
ReviewReplyNoteWrapper,
ReviewReplyCancelLink,
ReviewReplyLabel,
ReviewReplyNoteInfo,
ReviewReplyButton,
ReviewReplyButtonWrapper,
ReviewReplyMultilineTextField,
ReviewReplyImageUploadWrapper
};
//# sourceMappingURL=styles.js.map
/***/ }),
/***/ 76833:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
var __webpack_unused_export__;
__webpack_unused_export__ = ({ value: true });
const react_intl_1 = __webpack_require__(46984);
exports.A = (0, react_intl_1.defineMessages)({
ReviewReplyLabel: {
id: 'ReviewReplyNote.ReviewReplyLabel',
defaultMessage: 'Replying to:',
description: 'The label for the reply note field'
},
ReplyFieldPlaceHolder: {
id: 'ReviewReplyNote.ReplyFieldPlaceHolder',
defaultMessage: 'Add your reply here...',
description: 'The placeholder for the reply note text field'
},
ReplyButtonLabel: {
id: 'ReviewReplyNote.ReplyButtonLabel',
defaultMessage: 'Reply',
description: 'The label for the reply button'
},
CancelButtonLabel: {
id: 'ReviewReplyNote.CancelButtonLabel',
defaultMessage: 'Discard',
description: 'The label for the cancel button'
},
ReplyTextFieldLabel: {
id: 'ReviewReplyNote.ReplyTextFieldLabel',
defaultMessage: 'Your Reply',
description: 'The label for the reply text field'
},
AddReplySuccessToastMessage: {
id: 'ReviewReplyNote.AddReplySuccessToastMessage',
defaultMessage: 'Reply added',
description: 'Success message to show on reply save'
},
AddReplyFailedToastMessage: {
id: 'ReviewReplyNote.AddReplyFailedToastMessage',
defaultMessage: 'Unable to add your reply. Please try again.',
description: 'Failure message to show on reply save'
},
ReviewFieldAlertLimitErrorText: {
id: 'ReviewReplyNote.ReviewFieldAlertLimitErrorText',
defaultMessage: '_CHARACTER_LIMIT_CURRENT_ of _CHARACTER_LIMIT_ character limit remaining.',
description: 'The error message for the review field alert limit'
},
ReviewFieldMaxLimitErrorText: {
id: 'ReviewReplyNote.ReviewFieldMaxLimitErrorText',
defaultMessage: '_CHARACTER_LIMIT_ character limit met.',
description: 'The error message for the review field max limit'
},
ReviewFieldMinLimitErrorText: {
id: 'ReviewReplyNote.ReviewFieldMinLimitErrorText',
defaultMessage: 'Enter 2 characters or more to add a reply.',
description: 'The error message for the review field min limit'
}
});
//# sourceMappingURL=translations.js.map
/***/ }),
/***/ 39311:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const { fetchWithTimeout } = __webpack_require__(57743);
const { getFromNowDateFormat } = __webpack_require__(5697);
const isValidDate = (d) => d instanceof Date && !isNaN(d);
const fetchUserRecipeRating = async (recipeId, userId) => {
if (!userId)
return null;
let response;
try {
response = await fetchWithTimeout(
// pass ?verso=true to ensure API request is made to Verso not legacy site
`/api/recipe/${recipeId}/review-ratings/${userId}?verso=true`, { method: 'GET' }, 5000);
}
catch (err) {
console.error(err);
return null;
}
if (response.status === 200) {
const { reviews: originalReviews, error } = await response.json();
if (error) {
console.error(error);
}
else if (originalReviews && originalReviews.length > 0) {
const reviews = originalReviews
.filter((review) => {
// Ignore any review that does not have a rating
return Object.hasOwnProperty.call(review, 'rating');
})
.sort((reviewA, reviewB) => {
const reviewADate = new Date(reviewA.updatedAt);
const reviewBDate = new Date(reviewB.updatedAt);
return reviewBDate - reviewADate;
});
return reviews[0]?.rating || null;
}
}
return null;
};
const formatReviewDateAgo = (date, formatMessage) => {
const itemDate = new Date(date);
if (!isValidDate(itemDate)) {
return date;
}
return getFromNowDateFormat({
date: itemDate,
formatMessage,
includeHourAndMin: false
});
};
const transformTags = (tags, tagsInfo) => {
return tags.reduce((acc, tag) => {
const tagInfo = tagsInfo.find((taginfo) => taginfo.slug === tag);
if (tagInfo) {
acc.push(tagInfo.label);
}
return acc;
}, []);
};
const formatReviewListItems = (items, formatMessage, tagsInfo) => {
if (!items)
return [];
return items.map((review, index) => {
const { revisionId, viewerActionPresence, reactionCount, replyPageInfo, replies, role, storyURL, images } = review;
const item = {
id: index,
revisionId,
commentId: review._id,
viewerActionPresence,
reactionCount,
replyPageInfo,
replies,
role,
storyURL,
images
};
if (review.reviewText)
item.text = review.reviewText;
if (review.location)
item.location = review.location;
const reviewerInfo = review.isAnonymous
? 'Anonymous'
: review.siteUsername || review.reviewerInfo;
if (reviewerInfo)
item.username = reviewerInfo;
if (review.rating)
item.rating = review.rating;
if (review.recipeId)
item.recipeId = review.recipeId;
if (review.updatedAt)
item.date = formatReviewDateAgo(review.updatedAt, formatMessage);
if (review.tags)
item.tags = transformTags(review.tags, tagsInfo);
if (review.images)
item.images = review.images;
return item;
});
};
const INFO_SLICE_ITEMS_LABELS = {
yield: 'Yield',
totalTime: 'Total Time'
};
const formatInfoSliceItems = (times, formatMessage, translations) => {
if (!times)
return [];
const items = [];
Object.keys(times).forEach((key) => {
if (times[key] && times[key].length && INFO_SLICE_ITEMS_LABELS[key]) {
items.push({
key: translations[INFO_SLICE_ITEMS_LABELS[key]]
? formatMessage(translations[INFO_SLICE_ITEMS_LABELS[key]])
: INFO_SLICE_ITEMS_LABELS[key],
value: times[key]
});
}
});
return items;
};
module.exports = {
fetchUserRecipeRating,
formatReviewListItems,
formatInfoSliceItems,
formatReviewDateAgo
};
//# sourceMappingURL=utils.js.map
/***/ }),
/***/ 76661:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.uploadImage = void 0;
const fetchWithTimeout_1 = __webpack_require__(57743);
const cookie_1 = __webpack_require__(56892);
const cookies_1 = __importDefault(__webpack_require__(53788));
const MAX_TIMEOUT = 180000;
const uploadImage = async (options) => {
const { file, brand, product, expirationDate, pixVaultUrl } = options;
try {
const uploadUrl = `${pixVaultUrl}/upload`;
const token = (0, cookie_1.getCookie)(cookies_1.default.oidcCookies.access);
if (!token) {
throw new Error('Authentication token not found');
}
const formData = new FormData();
formData.append('files', file);
formData.append('brand', brand);
formData.append('product', product);
if (expirationDate) {
formData.append('expirationDate', expirationDate);
}
const response = await (0, fetchWithTimeout_1.fetchWithTimeout)(uploadUrl, {
body: formData,
method: 'POST',
headers: {
Authorization: `Bearer ${token}`
}
}, MAX_TIMEOUT);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || `Server responded with status: ${response.status}`);
}
const result = await response.json();
return result;
}
catch (error) {
console.error('Upload error details:', error);
throw error;
}
};
exports.uploadImage = uploadImage;
//# sourceMappingURL=pixvault-service.js.map
/***/ }),
/***/ 76497:
/***/ ((module) => {
// mutation for create username
const createUsername = /* GraphQL */ `
mutation createUsername($input: CreateSiteUsernameInput!) {
createSiteUsername(input: $input) {
siteUsername {
name
siteID
}
}
}
`;
const updateUsername = /* GraphQL */ `
mutation updateSiteUsername($input: UpdateSiteUsernameInput!) {
updateSiteUsername(input: $input) {
siteUsername {
name
siteID
}
}
}
`;
// query to get username
const getUsername = /* GraphQL */ `
query getSiteUsername($authorID: String!, $siteID: String) {
siteUsername(authorID: $authorID, siteID: $siteID) {
id
name
tenantID
authorID
}
}
`;
module.exports = {
getUsername,
createUsername,
updateUsername
};
//# sourceMappingURL=queries.js.map
/***/ }),
/***/ 67116:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.validate = exports.createNewUsername = exports.checkUsername = exports.requestGraphService = void 0;
const queries_1 = __webpack_require__(76497);
const Joi = __webpack_require__(16075);
const { GraphQLClient } = __webpack_require__(96497);
const { getCookie } = __webpack_require__(56892);
const cookies = (__webpack_require__(53788)["default"]);
const { oidcCookies } = cookies;
const LENGTH_ERROR = 'lengthError';
const SPECIAL_CHAR_ERROR = 'specialCharError';
const requestGraphService = (commentingUrl, options) => {
const token = getCookie(oidcCookies.access);
const client = new GraphQLClient(commentingUrl);
const { query, variables } = options;
const headers = {
'Access-Control-Request-Method': 'POST',
'Access-Control-Request-Headers': 'Content-Type',
'Content-Type': 'application/json',
'User-Agent': 'verso-client',
Authorization: `Bearer ${token}`
};
return client.request(query, variables, headers);
};
exports.requestGraphService = requestGraphService;
const checkUsername = async (authorID, siteID, commentingUrl) => {
if (!commentingUrl) {
console.error('A commenting url is required to check the username');
return undefined;
}
const options = {
operationName: 'getSiteUsername',
query: queries_1.getUsername,
variables: {
authorID,
siteID
}
};
let siteUsername;
try {
const response = await (0, exports.requestGraphService)(commentingUrl, options);
if (response?.siteUsername?.length === 0) {
siteUsername = null;
}
else {
siteUsername = response && response.siteUsername[0]?.name;
}
}
catch (error) {
console.log(`Error making GQL request in checkUsername: ${error.message}`);
}
return siteUsername;
};
exports.checkUsername = checkUsername;
const createNewUsername = async (params, logger) => {
const { name, organizationId, userId: authorID, url, action } = params;
const input = {
siteUsername: {
name,
siteID: organizationId,
authorID
},
clientMutationId: '0'
};
const options = {
operationName: action === 'UPDATE' ? 'updateSiteUsername' : 'createSiteUsername',
query: action === 'UPDATE' ? queries_1.updateUsername : queries_1.createUsername,
variables: {
input
}
};
try {
const response = await (0, exports.requestGraphService)(url, options);
const newSiteUsername = response &&
response[action === 'UPDATE' ? 'updateSiteUsername' : 'createSiteUsername']?.siteUsername?.name;
return newSiteUsername;
}
catch (error) {
// this error check should be replaced with the status code
let errorCode;
if ((error.response?.data === null &&
error.response?.errors[0]?.message ===
'InternalDevelopmentError: user or username already exists') ||
error.response?.errors[0]?.message ===
'InternalDevelopmentError: Duplicate Brand Username') {
errorCode = 'already_taken';
}
logger.warn(`Error making GQL request in createNewUsername: ${error?.response?.errors[0]?.message}`);
throw new Error(errorCode);
}
};
exports.createNewUsername = createNewUsername;
const validate = (input, validationRules) => {
const { minLength, maxLength } = validationRules;
const lengthValidation = Joi.string()
.min(minLength)
.max(maxLength)
.required();
const specialCharValidation = Joi.string()
.regex(/^[a-zA-Z0-9_]+$/)
.required();
// Validate length
const lengthValidationResult = lengthValidation.validate(input);
if (lengthValidationResult.error) {
return LENGTH_ERROR; // Return error type
}
// Validate special characters
const specialCharValidationResult = specialCharValidation.validate(input);
if (specialCharValidationResult.error) {
return SPECIAL_CHAR_ERROR; // Return error type
}
// If both validations pass, return null (indicating no error)
return null;
};
exports.validate = validate;
//# sourceMappingURL=utils.js.map
/***/ })
}]);