Xây dựng một ứng dụng chat sử dụng gRPC và React phần 2

Post on: 2023-05-10 23:25:57 | in: Golang
Tiếp bước phần 1 chúng ta xây dựng ứng dụng chat sử dụng gRPC và React

Chat

Mở Chat/index.js và dán mã sau:

import "./Chat.css";

export default function Chat({ msgList, sendMessage }) {
  function handler() {
    var msg = window.msgTextArea.value;
    sendMessage(msg);
    window.msgTextArea.value = "";
  }

  return (
    <div className="chat">
      <div className="chat-header">
        <h3>Group Messages</h3>
      </div>
      <div className="chat-list">
        {msgList?.map((chat, i) => (
          <ChatCard chat={chat} key={i} />
        ))}
      </div>
      <div className="chat-input">
        <div style={{ flex: "3 1 90%" }}>
          <textarea id="msgTextArea" />
        </div>
        <div
          style={{
            paddingLeft: "5px",
            display: "flex",
            alignItems: "center",
            justifyContent: "flex-end",
          }}
        >
          <button onClick={handler}>Send</button>
        </div>
      </div>
    </div>
  );
}

function ChatCard({ chat }) {
  return (
    <>
      <div style={{ fontSize: "9px", marginLeft: "4px", paddingLeft: "8px" }}>
        <span>{chat?.from}</span>
      </div>
      <div
        className={
          chat?.mine ? "chatcard chatcard-mine" : "chatcard chatcard-friend"
        }
      >
        <div className="chatcard-msg">
          <span>{chat?.msg}</span>
        </div>
        <div className="chatcard-time">
          <span>{chat?.time}</span>
        </div>
      </div>
    </>
  );
}

Nó giải tham chiếu (destructures) mảng msgList và hàm sendMesgage từ props của nó. Giao diện có một ô đầu vào và nút để gửi tin nhắn. Khi nút được nhấn, nó gọi hàm xử lý (handler function), trích xuất văn bản trò chuyện từ hộp đầu vào và gọi hàm sendMessage với văn bản trò chuyện làm tham số.

Giao diện cũng hiển thị các tin nhắn trong mảng msgList. Nó lặp lại từng tin nhắn và hiển thị mỗi tin nhắn trong thành phần ChatCard. Xem rằng thành phần ChatCard sử dụng props mine trong trò chuyện để biết cách hiển thị cho các tin nhắn thuộc về người dùng và những tin nhắn không thuộc về người dùng. Lớp CSS chatcard-mine được đặt cho các tin nhắn được gửi bởi người dùng, trong khi lớp CSS chatcard-friend được đặt cho các tin nhắn được gửi bởi người dùng khác.

Mở Chat.css và dán đoạn mã CSS sau:


.chat-input {
  display: flex;
  justify-content: space-between;
}

.chat-header {
  height: 30px;
  padding: 4px;
  background-color: rgb(53 151 234);
  color: white;
}

.chat-header h3 {
  margin: 4px;
}

.chat-list {
  padding: 4px;
  height: 400px;
  overflow: scroll;
}

.chat-input {
  margin: 4px 0;
  margin-left: 4px;
}

.chat-input textarea {
  border-radius: 20px;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  outline: none;
  width: 100%;
  box-sizing: content-box;
}

.chat-input button {
  border-radius: 50px;
}

.chatcard {
  background-color: dodgerblue;
  padding: 8px;
  border-radius: 14px;
  margin: 4px;
  color: white;
}

.chatcard-mine {
  border-bottom-right-radius: 0;
}

.chatcard-friend {
  border-top-left-radius: 0;
  background-color: forestgreen;
}

.chatcard-msg {
  padding: 4px;
}
​​​​​​​
.chatcard-time {
  text-align: right;
  font-size: 10px;
}

Header

Đoạn mã này chỉ đơn giản hiển thị tiêu đề cho ứng dụng trò chuyện. Thêm đoạn mã sau vào Header/index.js:


import "./Header.css";
​​​​​​​
export default function Header() {
  return (
    <section className="header">
      <div className="headerName">Chat</div>
    </section>
  );
}

Đặt kiểu cho nó trong Header.css của nó.


.header {
  height: 54px;
  background-color: rgb(53 151 234);
  color: white;
  display: flex;
  align-items: center;
  padding: 10px;
  font-family: sans-serif;
  padding-left: 27%;
}
​​​​​​​
.headerName {
  font-size: 1.8em;
}

Set the global styles

Mở index.css và dán đoạn mã sau:


body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
    "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
    "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background-color: rgba(234, 238, 243, 1);
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
    monospace;
}

button {
  height: 30px;
  padding: 0px 15px 2px;
  font-weight: 400;
  font-size: 1rem;
  line-height: normal;
  border-radius: 2px;
  cursor: pointer;
  outline: 0px;
  color: rgb(255, 255, 255);
  text-align: center;
  margin: 3px;
  background-color: rgb(53 151 234);
  border: 1px solid rgb(28 90 152);
}

.btn-danger {
  background-color: rgb(195 18 18);
  border: 1px solid rgb(195 18 18);
}

.container {
  min-height: 100vh;
  /*padding: 0 0.5rem; */
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;

  background-color: rgba(234, 238, 243, 1);
}

.main {
  /*padding: 5rem 0;*/
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 46%;
  /*justify-content: center;
  align-items: center;*/
}

.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 1000;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}

.modal-backdrop {
  opacity: 0.5;
  width: inherit;
  height: inherit;
  background-color: grey;
  position: fixed;
}

.modal-body {
  padding: 5px;
  padding-top: 15px;
  padding-bottom: 15px;
}

.modal-footer {
  padding: 15px 5px;
  display: flex;
  justify-content: space-between;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.modal-header h3 {
  margin: 0;
}

.modal-content {
  background-color: white;
  z-index: 1;
  padding: 10px;
  margin-top: 10px;

  width: 520px;
  box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2), 0px 24px 38px 3px rgba(0, 0, 0, 0.14),
    0px 9px 46px 8px rgba(0, 0, 0, 0.12);
  border-radius: 4px;
}

input[type="text"] {
  width: 100%;
  padding: 9px;
  font-weight: 400;
  cursor: text;
  outline: 0px;
  border: 1px solid rgb(120 121 121);
  border-radius: 5px;
  color: rgb(51, 55, 64);
  background-color: transparent;
  box-sizing: border-box;
}

.label {
  padding: 4px 0;
  font-size: small;
  color: rgb(51, 55, 64);
}

.content {
  display: flex;
  flex-wrap: wrap;
  flex-direction: column;
}

.inputField {
  margin: 3px 7px;
  flex: 1 40%;
}

button:disabled,
button[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

a[href] {
  text-decoration: none;
  color: black;
}

a:visited {
  color: black;
}
​​​​​​​
@media (min-width: 700px) {
  .main {
    width: 97%;
  }
}

Nếu máy chủ create-react-app của bạn chưa chạy, hãy khởi động nó:

yarn start
# or
npm run start

Mở trình duyệt yêu thích của bạn và điều hướng đến localhost:3000. Mở một trình duyệt khác cạnh nhau với trình duyệt đầu tiên. Sau đó, tham gia vào cuộc trò chuyện với các tên người dùng khác nhau và bắt đầu trò chuyện. Bạn sẽ thấy các tin nhắn được gửi nhanh chóng trong thời gian thực. Đó là sức mạnh của gRPC.

Source code

Tìm các kho mã nguồn của máy chủ gRPC và khách React.js dưới đây:

Tổng kết

Chúng ta đã tìm hiểu về gRPC. Chúng ta bắt đầu bằng cách xem xét một chút về lịch sử của nó và sau đó là cách nó hoạt động. Chúng ta đã tìm hiểu về gRPC streaming, cho phép chúng ta phát trực tiếp và lắng nghe một chuỗi dữ liệu. Chúng ta đã chứng minh tất cả điều này bằng cách xây dựng một ứng dụng trò chuyện trong React.js, trong quá trình đó, chúng ta cũng đã tìm hiểu về việc chuyển hướng yêu cầu từ một khách hàng trình duyệt bằng cách sử dụng Envoy, chúng ta đã tìm hiểu một chút về cấu hình Envoy và chúng ta đã có thể giao tiếp với một máy chủ gRPC từ trình duyệt. Đó là rất nhiều thứ chúng ta đã học trong bài hướng dẫn này.

gRPC mở ra một thế giới mới hoàn toàn của các khả năng và đồng thời khai thác sức mạnh tuyệt vời của HTTP/2.