Adding rating stars to chat - createIT
Get a free advice now!

    Pick the topic
    Developer OutsourcingWeb developingApp developingDigital MarketingeCommerce systemseEntertainment systems

    Thank you for your message. It has been sent.

    Adding rating stars to chat

    February 23, 2022
    Last update: February 13, 2023
    4 min read
    8
    0
    0
    Adding rating stars to chat

    CHALLENGE: allow chat members to rate chat by clicking stars

    SOLUTION: prepare a custom message type with custom rendering

    Building your own chat using SendBird? Do you want to get user rating at the end of a chat conversation? This tutorial will show you how.

    Typically, a Chat is a conversation between 2 users: the Client and the Agent or the Client and the Bot. It will be nice to allow the Client to rate the quality of the chat experience. We’re going to render clickable stars, which can be clicked only once. By clicking, the user will send a rate value as a response.

    ChannelList with default channel

    By default, channels are rendered from newest to oldest (based on channel creation time). We will use the sortChannelList parameter and place our DEFAULT_CHANNEL_URL as the first item on the list. It will also be autoloaded on page refresh ( setCurrentChannelUrl() ).

    Keep messages styling

    To keep messages styling included in React UIKit, we’re going to use renderCustomMessage prop that allows to change the render of only one customType, keeping other messages without change. Alternatively, we can overwrite renderChatItem, but the result will be the loss of all messages styling, removing reaction emoticons, typing indicator and the “message received” icon.

    Message HOC (High Order Component) expects a string as a returned value, so instead of using Component, renderStars will be a javascript function.

    Chat windows with star ratings

    Chat settings

    Make sure to change settings to your own values. You can also control the amount of displayed star icons.

    const APP_ID = "AAA";
    const USER_ID = "BBB";
    const DEFAULT_CHANNEL_URL = "CCC";
    const TOTAL_STARS = 10;

    RatingStars Message

    SendBirdProvider is a wrapper component for our customizations. Final changes include: changing the order of channel list and changing the rendering of one message type. The function is expecting: message.customType to be defined as “type2”. For this condition, starts will be rendered.

    // App.jsx
    import React, {useState, useCallback} from 'react';
    import {
      SendBirdProvider,
      Channel,
      ChannelSettings,
      ChannelList
    } from 'sendbird-uikit';
    import "sendbird-uikit/dist/index.css";
    import './App.css';
    import {renderStars} from "./renderStars";
    const APP_ID = "AAA";
    const USER_ID = "BBB";
    const DEFAULT_CHANNEL_URL = "CCC";
    const TOTAL_STARS = 10;
    export default function App() {
      const channelSort = useCallback((channels) => { // useCallback to memoize the fn
        if (channels.length === 0 ) {
          return channels;
        }
        const channel = channels.find(c => c.url === DEFAULT_CHANNEL_URL);
        if (!channel) {
          return channels;
        }
        const otherChannels = channels.filter(c => c.url !== channel.url);
        otherChannels.sort(function(a, b){
          if(a.name < b.name) { return -1; }
          if(a.name > b.name) { return 1; }
          return 0;
        });
        return [channel, ...otherChannels];
      }, []);
      const [currentChannelUrl, setCurrentChannelUrl] = useState("");
      return (
          <div className="App">
            <SendBirdProvider appId={APP_ID} userId={USER_ID} >
              <div style={{ display: 'flex', height: '100%' }}>
                <Channel
                    channelUrl={currentChannelUrl}
                    renderCustomMessage={(message, channel) => {
                      if (message.customType === 'type2') {
                        return (
                            renderStars(currentChannelUrl, TOTAL_STARS)
                        )
                      }
                    }}
                />
                <ChannelSettings channelUrl={currentChannelUrl} />
                <ChannelList
                    sortChannelList={channelSort}
                    onChannelSelect={(channel) => {
                      if (channel && channel.url) {
                        setCurrentChannelUrl(channel.url);
                      }
                    }}
                />
              </div>
            </SendBirdProvider>
          </div>
      );
    }

    Basic styling and disabling ability to rate the second time.

    .App {
      font-family: sans-serif;
      text-align: center;
      height: calc(100vh - 100px);
    }
    .sendbird-conversation {
      max-width:50%;
    }
    .starsRating {
      display:block;
      margin:2px 0;
    }
    .star {
      display:inline-block;
      font-size:2rem;
      color: #ff9933;
      cursor: pointer;
    }
    .starsRating.disabled {
      pointer-events:none;
      opacity: 0.5;
    }

    The component for sending a message (when a Star is clicked):

    // SendMessage.jsx
    import React, { useEffect } from "react";
    import {
        withSendBird,
        sendBirdSelectors,
    } from "sendbird-uikit";
    const CustomComponent = (props) => {
        const {
            msg,
            customType,
            channelUrl,
            extraData,
            sendMessage,
            updateLastMessage,
            sdk,
        } = props;
        const sendSelectTypeMessage = (msg, customType, channelUrl, extraData = null) => {
            const params = new sdk.UserMessageParams();
            if(!msg){
                return;
            }
            if(customType){
                params.customType = customType;
            }
            params.message = msg;
            if(extraData){
                params.data = extraData;
            }
            sendMessage(channelUrl, params)
                .then(message => {
                })
                .catch(e => {
                    console.warn(e);
                });
        }
        useEffect(() => {
            sendSelectTypeMessage(msg, customType, channelUrl, extraData);
        });
        return(
            <></>
        );
    };
    const SendMessageWithSendBird = withSendBird(CustomComponent, (state) => {
        const sendMessage = sendBirdSelectors.getSendUserMessage(state);
        const updateLastMessage = sendBirdSelectors.getUpdateUserMessage(state);
        const sdk = sendBirdSelectors.getSdk(state);
        return ({
            sendMessage,
            updateLastMessage,
            sdk
        });
    });
    export default SendMessageWithSendBird;

    And the most interesting part of the code – the main function for rendering stars, handling hover event and triggering sending a message.

    // renderStars.jsx
    import React, {useState, useEffect} from 'react';
    import SendMessageWithSendBird from "./SendMessage";
    /**
     * Stars rating
     */
    const Star = ({ marked, starId }) => {
        return (
            <span data-star-id={starId} className="star" role="button">
                  {marked ? '\u2605' : '\u2606'}
                </span>
        );
    };
    const StarRating = (props) => {
        const {channelUrl, total} = props;
        const [rating, setRating] = useState( 0);
        const [selection, setSelection] = useState(0);
        const [msgToSent, setMsgToSent] = useState({msg: "",msgType: "", currentRating:""});
        useEffect(() => {
            if(msgToSent.currentRating){
                setRating(msgToSent.currentRating);
            }
            // eslint-disable-next-line
        }, [msgToSent]);
        const hoverOver = event => {
            if(rating) {
              // do nothing
            } else {
                let val = 0;
                if (event && event.target && event.target.getAttribute('data-star-id')){
                    val = event.target.getAttribute('data-star-id');
                }
                setSelection(val);
            }
        };
        const component = !rating && msgToSent.msg ? (<SendMessageWithSendBird msg={msgToSent.msg} customType={msgToSent.msgType}  channelUrl={channelUrl} />) : (<></>);
        const classNameDisabled = rating ? "disabled" : "";
        return (
            <span
                onMouseOut={() => hoverOver(null)}
                onClick={e => {
                    if(rating) {
                        return false;
                    }
                    const currentRating = e.target.getAttribute('data-star-id') || rating;
                    setMsgToSent({msg: currentRating, msgType: "answer", currentRating: currentRating});
                }}
                onMouseOver={hoverOver}
                className={"starsRating " + classNameDisabled }
            >
                    {Array.from({ length: total }, (v, i) => (
                        <Star
                            starId={i + 1}
                            key={`star_${i + 1}`}
                            marked={selection ? selection >= i + 1 : rating >= i + 1}
                        />
                    ))}
                {component}
                </span>
        );
    };
    export const renderStars = (channelUrl, total) => {
        return () => (
            <StarRating channelUrl={channelUrl} total={total} />
        );
    }

    Conclusion

    Chat now has the ability to render a custom view for message customType. The stars functionality does not use any dependencies, you can easily change the color using CSS. The inspiration for this article was a snippet found here: https://www.30secondsofcode.org/react/s/star-rating

    Animation of chat window with star rating

    That’s it for today’s tutorial. Make sure to follow us for other useful guidelines and don’t forget to sign up for our newsletter.

    Support – Tips and Tricks
    All tips in one place, and the database keeps growing. Stay up to date and optimize your work!

    Contact us