Skip to content

File tree

6 files changed

+164
-36
lines changed

6 files changed

+164
-36
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React, { ReactElement, useContext, useRef } from 'react';
2+
import { LocalizationContext } from '../../../../lib/LocalizationContext';
3+
import Modal from '../../../../ui/Modal';
4+
import Button, { ButtonTypes } from '../../../../ui/Button';
5+
import Label, { LabelColors, LabelTypography } from '../../../../ui/Label';
6+
import '../MessageFeedbackModal/index.scss';
7+
import { useKeyDown } from '../../../../hooks/useKeyDown/useKeyDown';
8+
9+
export interface MessageFeedbackFailedModalProps {
10+
text: string;
11+
onCancel?: () => void;
12+
}
13+
14+
export default function MessageFeedbackFailedModal(props: MessageFeedbackFailedModalProps): ReactElement {
15+
const {
16+
text,
17+
onCancel,
18+
} = props;
19+
20+
const { stringSet } = useContext(LocalizationContext);
21+
22+
const modalRef = useRef(null);
23+
const onKeyDown = useKeyDown(modalRef, {
24+
Enter: () => onCancel?.(),
25+
Escape: () => onCancel?.(),
26+
});
27+
28+
return (
29+
<div onKeyDown={onKeyDown}>
30+
<Modal
31+
contentClassName='sendbird-message-feedback-modal-content__mobile'
32+
type={ButtonTypes.PRIMARY}
33+
onSubmit={onCancel}
34+
onClose={onCancel}
35+
submitText={stringSet.BUTTON__OK}
36+
renderHeader={() => (
37+
<div className='sendbird-modal__header'>
38+
<Label
39+
type={LabelTypography.H_1}
40+
color={LabelColors.ONBACKGROUND_1}
41+
className='sendbird-message-feedback-modal-header'
42+
>
43+
{ text }
44+
</Label>
45+
</div>
46+
)}
47+
customFooter={
48+
<div className='sendbird-message-feedback-modal-footer__root_failed'>
49+
<Button onClick={onCancel}>
50+
<Label type={LabelTypography.BUTTON_3} color={LabelColors.ONCONTENT_1}>
51+
{ stringSet.BUTTON__OK }
52+
</Label>
53+
</Button>
54+
</div>
55+
}
56+
/>
57+
</div>
58+
);
59+
}

src/modules/Channel/components/MessageFeedbackModal/index.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
align-items: center;
1414
}
1515

16+
.sendbird-message-feedback-modal-footer__root_failed {
17+
display: flex;
18+
justify-content: flex-end;
19+
align-items: center;
20+
margin-top: 48px;
21+
}
22+
1623
.sendbird-message-feedback-modal-footer__right-content {
1724
display: flex;
1825
gap: 8px;

src/modules/Channel/components/MessageFeedbackModal/index.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,56 @@ import { useKeyDown } from '../../../../hooks/useKeyDown/useKeyDown';
1313
export interface MessageFeedbackModalProps {
1414
selectedFeedback: FeedbackRating;
1515
message: CoreMessageType;
16-
onCancel?: () => void;
16+
onClose?: () => void;
1717
onSubmit?: (comment: string) => void;
18+
onUpdate?: (comment: string) => void;
1819
onRemove?: () => void;
1920
}
2021

2122
export default function MessageFeedbackModal(props: MessageFeedbackModalProps): ReactElement {
2223
const {
2324
selectedFeedback,
2425
message,
25-
onCancel,
26+
onClose,
2627
onSubmit,
28+
onUpdate,
2729
onRemove,
2830
} = props;
2931

3032
const { stringSet } = useContext(LocalizationContext);
3133
const { isMobile } = useMediaQueryContext();
3234

3335
const isEdit = message?.myFeedback && selectedFeedback === message.myFeedback.rating;
36+
const onSubmitWrapper = () => {
37+
if (!selectedFeedback) return;
38+
const comment = inputRef.current.value ?? '';
39+
if (isEdit) {
40+
if (comment !== message.myFeedback.comment) {
41+
onUpdate?.(comment);
42+
} else {
43+
onClose?.();
44+
}
45+
} else if (!message.myFeedback) {
46+
onSubmit?.(comment);
47+
}
48+
};
3449

3550
const modalRef = useRef(null);
3651
const inputRef = useRef(null);
52+
3753
const onKeyDown = useKeyDown(modalRef, {
38-
Enter: () => onSubmit?.(inputRef.current.value ?? ''),
39-
Escape: () => onCancel?.(),
54+
Enter: () => onSubmitWrapper(),
55+
Escape: () => onClose?.(),
4056
});
4157

4258
return (
4359
<div onKeyDown={onKeyDown}>
4460
<Modal
4561
contentClassName='sendbird-message-feedback-modal-content__mobile'
4662
type={ButtonTypes.PRIMARY}
47-
onCancel={onCancel}
63+
onCancel={onClose}
4864
onSubmit={() => {
49-
onSubmit?.(inputRef.current.value ?? '');
65+
onSubmitWrapper();
5066
}}
5167
submitText={stringSet.BUTTON__SUBMIT}
5268
renderHeader={() => (
@@ -74,12 +90,12 @@ export default function MessageFeedbackModal(props: MessageFeedbackModalProps):
7490
: <div/>
7591
}
7692
<div className='sendbird-message-feedback-modal-footer__right-content'>
77-
<Button type={ButtonTypes.SECONDARY} onClick={onCancel}>
93+
<Button type={ButtonTypes.SECONDARY} onClick={onClose}>
7894
<Label type={LabelTypography.BUTTON_3} color={LabelColors.ONBACKGROUND_1}>
7995
{stringSet.BUTTON__CANCEL}
8096
</Label>
8197
</Button>
82-
<Button onClick={() => onSubmit?.(inputRef.current.value ?? '')}>
98+
<Button onClick={() => onSubmitWrapper()}>
8399
<Label type={LabelTypography.BUTTON_3} color={LabelColors.ONCONTENT_1}>
84100
{ isEdit ? stringSet.BUTTON__SAVE : stringSet.BUTTON__SUBMIT }
85101
</Label>

src/ui/FeedbackIconButton/index.scss

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,29 @@
1515
border-color: t(on-bg-4);
1616
}
1717

18+
&:hover {
19+
@include themed() {
20+
background-color: t(bg-1);
21+
}
22+
}
23+
1824
&.sendbird-iconbutton__feedback__disabled {
1925
cursor: not-allowed;
26+
27+
&:hover {
28+
background-color: transparent;
29+
}
2030
}
21-
}
2231

23-
.sendbird-iconbutton__feedback__pressed {
24-
@include themed() {
25-
border-color: t(primary-3);
26-
background-color: t(primary-3);
32+
&.sendbird-iconbutton__feedback__pressed {
33+
&:hover {
34+
background-color: transparent;
35+
}
36+
37+
@include themed() {
38+
border-color: t(primary-3);
39+
background-color: t(primary-3);
40+
}
2741
}
2842
}
2943

src/ui/Label/stringSet.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,17 @@ const stringSet = {
216216
FEEDBACK_LIKE: 'Like',
217217
FEEDBACK_DISLIKE: 'Dislike',
218218
// Mobile feedback options menu items
219-
EDIT_COMMENT: 'Edit Comment',
219+
EDIT_COMMENT: 'Edit comment',
220220
REMOVE_FEEDBACK: 'Remove Feedback',
221221
// Feedback modal title
222222
FEEDBACK_MODAL_TITLE: 'Provide additional feedback (optional)',
223223
FEEDBACK_CONTENT_PLACEHOLDER: 'Leave a comment',
224224
BUTTON__REMOVE_FEEDBACK: 'Remove feedback',
225+
// Feedback failed modal title
226+
FEEDBACK_FAILED_SUBMIT: 'Couldn’t submit. Try again.',
227+
FEEDBACK_FAILED_SAVE: 'Couldn’t save. Try again.',
228+
FEEDBACK_FAILED_DELETE: 'Couldn’t delete. Try again.',
229+
225230
},
226231
};
227232

src/ui/MessageContent/index.tsx

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, {
2-
ReactElement, ReactNode,
2+
ReactElement, ReactNode, useContext,
33
useRef,
44
useState,
55
} from 'react';
@@ -23,7 +23,7 @@ import {
2323
CoreMessageType,
2424
isMultipleFilesMessage,
2525
} from '../../utils';
26-
import { useLocalization } from '../../lib/LocalizationContext';
26+
import { LocalizationContext, useLocalization } from '../../lib/LocalizationContext';
2727
import useSendbirdStateContext from '../../hooks/useSendbirdStateContext';
2828
import { GroupChannel } from '@sendbird/chat/groupChannel';
2929
import { EmojiContainer } from '@sendbird/chat';
@@ -43,6 +43,7 @@ import FeedbackIconButton from '../FeedbackIconButton';
4343
import MobileFeedbackMenu from '../MobileFeedbackMenu';
4444
import MessageFeedbackModal from '../../modules/Channel/components/MessageFeedbackModal';
4545
import { SbFeedbackStatus } from './types';
46+
import MessageFeedbackFailedModal from '../../modules/Channel/components/MessageFeedbackFailedModal';
4647

4748
export interface MessageContentProps {
4849
className?: string | Array<string>;
@@ -141,6 +142,9 @@ export default function MessageContent(props: MessageContentProps): ReactElement
141142
const [selectedFeedback, setSelectedFeedback] = useState<FeedbackRating>(null);
142143
const [showFeedbackOptionsMenu, setShowFeedbackOptionsMenu] = useState(false);
143144
const [showFeedbackModal, setShowFeedbackModal] = useState(false);
145+
const [feedbackFailedText, setFeedbackFailedText] = useState('');
146+
147+
const { stringSet } = useContext(LocalizationContext);
144148

145149
const isByMe = (userId === (message as SendableMessageType)?.sender?.userId)
146150
|| ((message as SendableMessageType)?.sendingStatus === 'pending')
@@ -166,6 +170,11 @@ export default function MessageContent(props: MessageContentProps): ReactElement
166170
const isFeedbackEnabled = config?.groupChannel?.enableFeedback && isFeedbackMessage;
167171
const feedbackMessageClassName = isFeedbackEnabled ? 'sendbird-message-content__feedback' : '';
168172

173+
const onCloseFeedbackForm = () => {
174+
setSelectedFeedback(null);
175+
setShowFeedbackModal(false);
176+
};
177+
169178
// onMouseDown: (e: React.MouseEvent<T>) => void;
170179
// onTouchStart: (e: React.TouchEvent<T>) => void;
171180
// onMouseUp: (e: React.MouseEvent<T>) => void;
@@ -473,40 +482,58 @@ export default function MessageContent(props: MessageContentProps): ReactElement
473482
/>
474483
)
475484
}
476-
{/* Feedback Modal */}
485+
{/* Feedback modal */}
477486
{
478487
showFeedbackModal && (
479488
<MessageFeedbackModal
480489
selectedFeedback={selectedFeedback}
481490
message={message}
482491
onSubmit={async (comment: string) => {
483-
if (!selectedFeedback) {
484-
return;
485-
}
486-
if (!message.myFeedback) {
492+
try {
487493
await message.submitFeedback({
488494
rating: selectedFeedback,
489495
comment,
490496
});
491-
} else if (comment !== message.myFeedback.comment) {
492-
const newFeedback: Feedback = new Feedback({
493-
id: message.myFeedback.id,
494-
rating: selectedFeedback,
495-
comment,
496-
});
497-
await message.updateFeedback(newFeedback);
497+
} catch (error) {
498+
config?.logger?.error?.('Channel: Submit feedback failed.', error);
499+
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_SUBMIT);
498500
}
499-
setSelectedFeedback(null);
500-
setShowFeedbackModal(false);
501+
onCloseFeedbackForm();
501502
}}
502-
onCancel={() => {
503-
setSelectedFeedback(null);
504-
setShowFeedbackModal(false);
503+
onUpdate={async (comment: string) => {
504+
const newFeedback: Feedback = new Feedback({
505+
id: message.myFeedback.id,
506+
rating: selectedFeedback,
507+
comment,
508+
});
509+
try {
510+
await message.updateFeedback(newFeedback);
511+
} catch (error) {
512+
config?.logger?.error?.('Channel: Update feedback failed.', error);
513+
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_SAVE);
514+
}
515+
onCloseFeedbackForm();
505516
}}
517+
onClose={onCloseFeedbackForm}
506518
onRemove={async () => {
507-
await message.deleteFeedback(message.myFeedback.id);
508-
setSelectedFeedback(null);
509-
setShowFeedbackModal(false);
519+
try {
520+
await message.deleteFeedback(message.myFeedback.id);
521+
} catch (error) {
522+
config?.logger?.error?.('Channel: Delete feedback failed.', error);
523+
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_DELETE);
524+
}
525+
onCloseFeedbackForm();
526+
}}
527+
/>
528+
)
529+
}
530+
{/* Feedback failed modal */}
531+
{
532+
feedbackFailedText && (
533+
<MessageFeedbackFailedModal
534+
text={feedbackFailedText}
535+
onCancel={() => {
536+
setFeedbackFailedText('');
510537
}}
511538
/>
512539
)

0 commit comments

Comments
 (0)