Chat – send message on enter - Blog | 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.

    Chat – send message on enter

    April 6, 2022
    Last update: February 13, 2023
    5 min read
    10
    0
    0
    Chat – send message on enter

    We have a chat application that uses the SendBird API. Message input is customized, with custom HTML and styles from Material UI. The user can send a message or attach a file. File size is restricted to 1mb.

    Adding keyboard events

    This article was inspired by an example provided by the SendBird team: https://codesandbox.io/s/2-5-customizing-chatinput-wgi9d?file=/src/CustomizedMessageInput.js . Our improvements include: handling keyboard keys: ENTER will send a message immediately, ENTER + SHIFT will create a new line. Also, dependencies were upgraded to the new MUI Material version 5 and React 17.0.2.

    About Sendbird

    Founded in 2013, Sendbird developed a messaging-as-a-service API that provides chat, voice, and video messaging for mobile apps and websites. The company is headquartered in San Mateo, CA with additional offices in New York, London, Seoul, Singapore and Bengaluru. It has raised over $220M.

    The company was a member of the Winter 2016 Y Combinator class. During the acceleration program, its revenue grew 20 times, leading to funding from many investors, including: ICONIQ Capital, STEADFAST Capital Ventures, Tiger Global Management, Shasta Ventures, Softbank Vision Fund 2, and Y Combinator.

    30 days Trial plan

    To start using SendBird you can register for a fully operational trial plan and use it without any payment for 30 days. After this time, if you decide for a longer relationship, there are premium plans. The pricing depends on MAU (Monthly Active Chat Users) and starts from $499/month.

    Customized input

    Here is the full source code for customizing message input in the SendBird chat. You can use it to create a fully working chat with channels, reactions and file sending. Make sure to change APP_ID and USER_ID variables in const.js!

    // App.js
    import React, { useState } from "react";
    import { SendBirdProvider as SBProvider } from "sendbird-uikit";
    import "sendbird-uikit/dist/index.css";
    import { ButtonGroup, Button } from "@mui/material";
    import { APP_ID, USER_ID, NICKNAME } from "./const";
    import CustomizedApp from "./CustomizedApp";
    import "./index.css";
    import useStyles from "./styles";
    export default function App() {
      const classes = useStyles();
      const { selected, unselected, rightButton } = classes;
      const [isCustomizedInput, setIsCustomizedInput] = useState(false);
      return (
          <div className="app-wrapper">
            <div className="channel-selector">
              <div className="channel-selector__icons">
                <ButtonGroup>
                  <Button
                      className={isCustomizedInput ? unselected : selected}
                      onClick={() => setIsCustomizedInput(false)}
                      variant={isCustomizedInput ? "outlined" : "contained"}
                      size="large"
                  >
                    Normal Input
                  </Button>
                  <Button
                      className={`${
                          isCustomizedInput ? selected : unselected
                      } ${rightButton}`}
                      onClick={() => setIsCustomizedInput(true)}
                      variant={isCustomizedInput ? "contained" : "outlined"}
                      size="large"
                  >
                    Customized Input
                  </Button>
                </ButtonGroup>
              </div>
            </div>
            <SBProvider appId={APP_ID} userId={USER_ID} nickname={NICKNAME}>
              <CustomizedApp isCustomizedInput={isCustomizedInput} />
            </SBProvider>
          </div>
      );
    }

    Main app settings:

    // const.js
    // put your own APP_ID here
    // get your app_id -> https://dashboard.sendbird.com/auth/signin
    export const APP_ID = "AAA";
    // set your own USER_ID and NICKNAME
    export const USER_ID = "BBB";
    export const NICKNAME = "TestName123";

    Main function for rendering the entire chat:

    // CustomizedApp.jsx
    import React, { useState } from "react";
    import {
        Channel as SBConversation,
        ChannelList as SBChannelList,
        ChannelSettings as SBChannelSettings,
        withSendBird
    } from "sendbird-uikit";
    import CustomizedMessageInput from "./CustomizedMessageInput";
    function CustomizedApp(props) {
        // props
        const { isCustomizedInput } = props;
        // useState
        const [currentChannelUrl, setCurrentChannelUrl] = useState("");
        const [showSettings, setShowSettings] = useState(false);
        return (
            <div className="customized-app">
                <div className="sendbird-app__wrap">
                    <div className="sendbird-app__channellist-wrap">
                        <SBChannelList
                            onChannelSelect={(channel) => {
                                if (channel && channel.url) {
                                    setCurrentChannelUrl(channel.url);
                                }
                            }}
                        />
                    </div>
                    <div className="sendbird-app__conversation-wrap">
                        <SBConversation
                            onChatHeaderActionClick={() => {
                                if (showSettings) {
                                    setShowSettings(false);
                                } else {
                                    setShowSettings(true);
                                }
                            }}
                            channelUrl={currentChannelUrl}
                            renderMessageInput={
                                isCustomizedInput
                                    ? ({ channel, user, disabled }) => (
                                        <CustomizedMessageInput
                                            channel={channel}
                                            user={user}
                                            disabled={disabled}
                                        />
                                    )
                                    : null
                            }
                        />
                    </div>
                </div>
                {showSettings && (
                    <div className="sendbird-app__settingspanel-wrap">
                        <SBChannelSettings
                            channelUrl={currentChannelUrl}
                            onCloseClick={() => {
                                setShowSettings(false);
                            }}
                        />
                    </div>
                )}
            </div>
        );
    }
    export default withSendBird(CustomizedApp);

    And customized send message input that is styled using MUI Material 5:

    // CustomizedMessageInput.jsx
    import React, { useState } from "react";
    import { sendBirdSelectors, withSendBird } from "sendbird-uikit";
    import {
        InputAdornment,
        IconButton,
        FormControl,
        InputLabel,
        OutlinedInput
    } from '@mui/material';
    import {
        AttachFile as AttachFileIcon,
        Send as SendIcon
    } from "@mui/icons-material";
    import { makeStyles } from '@mui/styles';
    const useStyles = makeStyles({
        input: {
            display: "none"
        }
    });
    function CustomizedMessageInput(props) {
        const classes = useStyles();
        // props
        const {
            channel,
            disabled,
            // from mapStoreToProps
            sendUserMessage,
            sendFileMessage,
            sdk
        } = props;
        // state
        const [inputText, setInputText] = useState("");
        const [isShiftPressed, setIsShiftPressed] = useState(false);
        const isInputEmpty = inputText.length < 1;
        const KeyCode = {
            SHIFT: 16,
            ENTER: 13,
        };
        // event handler
        const handleChange = event => {
            setInputText(event.target.value);
        };
        const sendFileMessage_ = event => {
            if (event.target.files && event.target.files[0]) {
                console.log(event.target.files[0]);
                // Implement your custom validation here
                if (event.target.files[0].size > 1 * 1000 * 1000) {
                    alert("Image size greater than 1 MB");
                    return;
                }
                const params = new sdk.FileMessageParams();
                params.file = event.target.files[0];
                sendFileMessage(channel.url, params)
                    .then(message => {
                        console.log(message);
                        event.target.value = "";
                    })
                    .catch(error => {
                        console.log(error.stack);
                    });
            }
        };
        const sendUserMessage_ = event => {
            const params = new sdk.UserMessageParams();
            params.message = inputText;
            sendUserMessage(channel.url, params)
                .then(message => {
                    console.log(message);
                    setInputText("");
                })
                .catch(error => {
                    console.log(error.message);
                });
        };
        return (
            <div className="customized-message-input">
                <FormControl variant="outlined" disabled={disabled} fullWidth>
                    <InputLabel htmlFor="customized-message-input">User Message</InputLabel>
                    <OutlinedInput
                        id="customized-message-input"
                        type="txt"
                        value={inputText}
                        onChange={handleChange}
                        onKeyDown={(e) => {
                            if (e.keyCode === KeyCode.SHIFT) {
                                setIsShiftPressed(true);
                            }
                            if (!isShiftPressed && e.keyCode === KeyCode.ENTER) {
                                e.preventDefault();
                                sendUserMessage_();
                            }
                        }}
                        onKeyUp={(e) => {
                            if (e.keyCode === KeyCode.SHIFT) {
                                setIsShiftPressed(false);
                            }
                        }}
                        multiline
                        endAdornment={
                            <InputAdornment position="end">
                                {isInputEmpty ? (
                                    <div className="customized-message-input__file-container">
                                        <input
                                            accept="image/*"
                                            id="icon-button-file"
                                            type="file"
                                            className={classes.input}
                                            onChange={sendFileMessage_}
                                        />
                                        <label htmlFor="icon-button-file">
                                            <IconButton
                                                color="primary"
                                                aria-label="upload picture"
                                                component="span"
                                                disabled={disabled}
                                            >
                                                <AttachFileIcon
                                                    color={disabled ? "disabled" : "primary"}
                                                />
                                            </IconButton>
                                        </label>
                                    </div>
                                ) : (
                                    <IconButton
                                        disabled={disabled}
                                        onClick={sendUserMessage_}
                                        // onMouseDown={sendUserMessage}
                                    >
                                        <SendIcon color={disabled ? "disabled" : "primary"} />
                                    </IconButton>
                                )}
                            </InputAdornment>
                        }
                    />
                </FormControl>
            </div>
        );
    }
    const mapStoreToProps = store => {
        const sendUserMessage = sendBirdSelectors.getSendUserMessage(store);
        const sdk = sendBirdSelectors.getSdk(store);
        const sendFileMessage = sendBirdSelectors.getSendFileMessage(store);
        return {
            sendUserMessage,
            sdk,
            sendFileMessage
        };
    };
    export default withSendBird(CustomizedMessageInput, mapStoreToProps);

    Let’s add some basic styles:

    /**
    index.css
     */
    .app-wrapper {
      height: calc(100vh - 20px);
    }
    .channel-selector {
      height: 10%;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .channel-selector__icons {
      display: flex;
    }
    .customized-app {
      height: 90%;
    }
    .customized-message-input {
      margin: 10px 20px 0 20px
    }
    .MuiInputLabel-formControl {
      background:#fff;
    }

    Regarding dependencies, we’re using a couple. The important ones are: sendbird-uikit and @mui/material. Full package.json file below:

    {
      "name": "my-app",
      "version": "0.1.0",
      "private": true,
      "description": "",
      "dependencies": {
        "@emotion/react": "^11.7.1",
        "@emotion/styled": "^11.6.0",
        "@mui/icons-material": "^5.3.1",
        "@mui/material": "^5.4.0",
        "@mui/styles": "^5.3.0",
        "@testing-library/jest-dom": "^5.16.1",
        "@testing-library/react": "^12.1.2",
        "@testing-library/user-event": "^13.5.0",
        "@types/jest": "^27.4.0",
        "@types/node": "^16.11.21",
        "@types/react": "^17.0.38",
        "@types/react-dom": "^17.0.11",
        "prop-types": "^15.7.2",
        "react": "17.0.2",
        "react-dom": "17.0.2",
        "react-scripts": "5.0.0",
        "sendbird-uikit": "2.5.3",
        "typescript": "^4.5.5",
        "web-vitals": "^2.1.4"
      },
      "devDependencies": {
        "typescript": "3.8.3"
      },
      "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
      },
      "eslintConfig": {
        "extends": [
          "react-app",
          "react-app/jest"
        ]
      },
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      }
    }

    Conclusion

    SendBird provides a toolbox for creating custom chats. Components can be customized and styled according to brand colors. If needed, we can also implement Video / Voice calls and check advanced analytics. By using webooks integrated with API, we can add ChatBots that will answer Client’s questions using AI (Machine Learning).

    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 to stay up to date.

    Technology
    Be on the same page as the rest of the industry.

    Contact us