Skip to content

Commit

Permalink
feat: add message feedback (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
AhyoungRyu authored Mar 27, 2024
1 parent 122b11a commit 6d0244c
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 25 deletions.
38 changes: 19 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"preview": "vite preview"
},
"dependencies": {
"@sendbird/chat": "^4.10.1",
"@sendbird/uikit-react": "^3.13.1",
"@sendbird/chat": "^4.11.0",
"@sendbird/uikit-react": "^3.13.4",
"@tanstack/react-query": "^5.17.19",
"dompurify": "^3.0.4",
"react-popper-tooltip": "^4.4.2",
Expand Down
163 changes: 163 additions & 0 deletions src/components/BotMessageFeedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { FeedbackRating, UserMessage, Feedback } from '@sendbird/chat/message';
import FeedbackIconButton from '@sendbird/uikit-react/ui/FeedbackIconButton';
import Icon, { IconTypes } from '@sendbird/uikit-react/ui/Icon';
import MessageFeedbackFailedModal from '@sendbird/uikit-react/ui/MessageFeedbackFailedModal';
import MessageFeedbackModal from '@sendbird/uikit-react/ui/MessageFeedbackModal';
import MobileFeedbackMenu from '@sendbird/uikit-react/ui/MobileFeedbackMenu';
import { useState } from 'react';

import { useConstantState } from '../context/ConstantContext';
import { isMobile } from '../utils';

function BotMessageFeedback({ message }: { message: UserMessage }) {
const { stringSet } = useConstantState();
const [showFeedbackOptionsMenu, setShowFeedbackOptionsMenu] =
useState<boolean>(false);
const [showFeedbackModal, setShowFeedbackModal] = useState<boolean>(false);
const [feedbackFailedText, setFeedbackFailedText] = useState<string>('');

const openFeedbackFormOrMenu = () => {
if (isMobile) {
setShowFeedbackOptionsMenu(true);
} else {
setShowFeedbackModal(true);
}
};

const onCloseFeedbackForm = () => {
setShowFeedbackModal(false);
};

return (
<>
{/** Feedback icons */}
<div className="sendbird-message-content__middle__body-container__feedback-buttons-container">
<FeedbackIconButton
isSelected={message?.myFeedback?.rating === FeedbackRating.GOOD}
onClick={async () => {
if (!message?.myFeedback?.rating) {
try {
await message.submitFeedback({
rating: FeedbackRating.GOOD,
});
openFeedbackFormOrMenu();
} catch (error) {
console.error?.('Channel: Submit feedback failed.', error);
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_SUBMIT);
}
} else {
openFeedbackFormOrMenu();
}
}}
disabled={
message?.myFeedback != null &&
message.myFeedback.rating !== FeedbackRating.GOOD
}
>
<Icon type={IconTypes.FEEDBACK_LIKE} width="24px" height="24px" />
</FeedbackIconButton>
<FeedbackIconButton
isSelected={message?.myFeedback?.rating === FeedbackRating.BAD}
onClick={async () => {
if (!message?.myFeedback?.rating) {
try {
await message.submitFeedback({
rating: FeedbackRating.BAD,
});
openFeedbackFormOrMenu();
} catch (error) {
console.error?.('Channel: Submit feedback failed.', error);
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_SUBMIT);
}
} else {
openFeedbackFormOrMenu();
}
}}
disabled={
message?.myFeedback != null &&
message.myFeedback.rating !== FeedbackRating.BAD
}
>
<Icon type={IconTypes.FEEDBACK_DISLIKE} width="24px" height="24px" />
</FeedbackIconButton>
</div>
{
// Feedback menu
message.myFeedback?.rating && showFeedbackOptionsMenu && (
<MobileFeedbackMenu
hideMenu={() => {
setShowFeedbackOptionsMenu(false);
}}
onEditFeedback={() => {
setShowFeedbackOptionsMenu(false);
setShowFeedbackModal(true);
}}
onRemoveFeedback={async () => {
try {
if (message.myFeedback?.id == null) {
throw new Error('Invalid feedback id');
}
await message.deleteFeedback(message.myFeedback.id);
} catch (error) {
console.error?.('Channel: Delete feedback failed.', error);
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_DELETE);
}
setShowFeedbackOptionsMenu(false);
}}
/>
)
}
{
// Feedback modal
message.myFeedback != null &&
message?.myFeedback?.rating &&
showFeedbackModal && (
<MessageFeedbackModal
selectedFeedback={message.myFeedback.rating}
message={message}
onUpdate={async (
selectedFeedback: FeedbackRating,
comment: string
) => {
const newFeedback: Feedback = new Feedback({
id: message.myFeedback.id,
rating: selectedFeedback,
comment,
});
try {
await message.updateFeedback(newFeedback);
} catch (error) {
console.error('Channel: Update feedback failed.', error);
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_SAVE);
}
onCloseFeedbackForm();
}}
onClose={onCloseFeedbackForm}
onRemove={async () => {
try {
await message.deleteFeedback(message.myFeedback.id);
} catch (error) {
console.error('Channel: Delete feedback failed.', error);
setFeedbackFailedText(stringSet.FEEDBACK_FAILED_DELETE);
}
onCloseFeedbackForm();
}}
/>
)
}
{
// Feedback failed modal
feedbackFailedText && (
<MessageFeedbackFailedModal
text={feedbackFailedText}
onCancel={() => {
setFeedbackFailedText('');
}}
/>
)
}
</>
);
}

export default BotMessageFeedback;
4 changes: 2 additions & 2 deletions src/components/BotMessageWithBodyInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import Label, {
import { ReactNode } from 'react';
import styled from 'styled-components';

import BotMessageFeedback from './BotMessageFeedback';
import BotProfileImage from './BotProfileImage';
import { SentTime, BodyContainer } from './MessageComponent';
import { ReactionContainer } from './ReactionContainer';
import { useConstantState } from '../context/ConstantContext';
import { formatCreatedAtToAMPM } from '../utils';
import { isLastMessageInStreaming } from '../utils/messages';
Expand Down Expand Up @@ -111,7 +111,7 @@ export default function BotMessageWithBodyInput(props: Props) {
displayProfileImage &&
!isBotWelcomeMessage &&
!(isLastBotMessage && isLastMessageInStreaming(message)) &&
!isFormMessage && <ReactionContainer message={message} />}
!isFormMessage && <BotMessageFeedback message={message} />}
</>
<SentTime>{formatCreatedAtToAMPM(message.createdAt)}</SentTime>
</Content>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const SBComponent = () => {
userNickName,
configureSession,
enableMention,
enableEmojiFeedback,
customUserAgentParam,
stringSet,
} = useConstantState();
Expand Down Expand Up @@ -89,6 +90,7 @@ const SBComponent = () => {
enableDocument: false,
},
enableVoiceMessage: false,
enableFeedback: enableEmojiFeedback,
},
}}
>
Expand Down
2 changes: 1 addition & 1 deletion src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export interface Constant {
enableMention: boolean;
enableMobileView: boolean;
firstMessageData: FirstMessageItem[];
stringSet: Partial<StringSet> | undefined;
stringSet: Partial<StringSet>;
}

export interface SuggestedReply {
Expand Down
6 changes: 5 additions & 1 deletion src/context/ConstantContext.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LabelStringSet } from '@sendbird/uikit-react/ui/Label';
import { createContext, useContext, useMemo } from 'react';

import { type Constant, DEFAULT_CONSTANT } from '../const';
Expand Down Expand Up @@ -67,7 +68,10 @@ export const ConstantStateProvider = (props: ProviderProps) => {
},
customUserAgentParam: props.customUserAgentParam,
configureSession: props.configureSession,
stringSet: props.stringSet,
stringSet: {
...LabelStringSet,
...props.stringSet,
},
enableSourceMessage:
props.enableSourceMessage ?? initialState.enableSourceMessage,
enableEmojiFeedback:
Expand Down
4 changes: 4 additions & 0 deletions src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@
.sendbird-message-input .sendbird-message-input-text-field {
overflow-y: hidden;
}

.sendbird-modal__content {
width: calc(100% - 20px);
}

0 comments on commit 6d0244c

Please sign in to comment.