チャットApp(chatroomのビュー)

今回はチャットルームの見た目を整えていきます。その為に最初にチャットルームを作った時点で1つか2つメッセージがないと見た目がわからないのでチャットルームを作る[username].tsxをまず編集します。

[username].tsx

const createChatroom = async() => {
   const owner:string = user.displayName;
   const date = Date.now();
   const chatroom:ChatroomType = {
       owner:  owner,
       roomname: "",
       member: [],
       chats:  [
           {
               text: `ここは、${ user.displayName }のチャットルームだよ!下のフォームから投稿してね!`,
               date: date,
               uid : "chat-bot",
               username: "chat-bot",
               photoURL: "",
           },
           {
               text: "了解したよ!",
               date: date,
               uid : user.uid,
               username: user.displayName,
               photoURL: user.photoURL,
           }
       ],
   }
   const msg = await setChatroomToFirestore(chatroom, user.uid);
   console.log(msg);
   const [getmsg, temp] = await getChatroomFromFirestore(user.uid);
   if (getmsg === "get chatroom successfully!") {
       // ボタンを表示するcssクラスを付与
       setRoomExist(true);
   } else {
       setRoomExist(false);
   }
}

編集した関数の部分だけ載せてます。chatsというプロパティの中に2つメッセージを入れただけです。一つはボット的な扱いでもう一つは自分のメッセージです。

スクリーンショット 2020-08-24 9.34.29

チャットルームのディレクトリ構造は上のようになっています。前回話しましたが、ログインしていない状態でチャットルームにアクセスするとsignin.tsxにリダイレクトします。メインはindex.tsxになるのでそこのコードを紹介していきます。

index.tsx

import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import BasicHead from "../../../components/atom/head";
import TitleLogo from "../../../components/atom/logo";
import BasicButton from "../../../components/atom/button";
import BasicH2 from "../../../components/atom/basicH2";
import UserField from "../../../components/compo/userField";
import ChatContainer from "../../../components/compo/chatContainer";
import Styles from "../../../styles/chatroom.module.css";
import { activeUserExist, getActiveUser } from "../../../functions/auth";
import { getChatroomFromFirestore, ChatroomType } from "../../../functions/database";
export default function Room() {
   const [ user, setUser] = useState<firebase.User>();
   const [ room, setRoom] = useState<ChatroomType>();
   const router = useRouter();
   // userの存在を確認
   const checkUser = async() => {
       if (await activeUserExist()) {
           const usr = getActiveUser();
           setUser(usr);
       } else {
           console.log("no user");
       }
   }
   // roomの存在を確認
   const checkroom = async() => {
       const path = location.pathname.split("/chatroom/")[1];
       const [msg, rm] = await getChatroomFromFirestore(path);
       if (msg === "get chatroom successfully!") {
           setRoom(rm);
       }
   }
   useEffect(() => {
       (async() => {
           await checkUser();
           await checkroom();
       })();
   }, []);
   const chatArea = () => {
       if (room) {
           return (
               <>
                   <div className={ Styles.roomname }>
                       <BasicH2>{ room.owner } のチャットルーム</BasicH2>
                   </div>
                   <div className={ Styles.chatarea }>
                       <div className={ Styles.chatareaInner }>
                           <div>
                               <ChatContainer
                                   chats={ room.chats }
                                   user={ user.uid }
                               />
                           </div>
                       </div>
                   </div>
               </>
           );
       }
       return (
           <div className={ Styles.chatareaInner }>
               <BasicH2>
                   このチャットルームは存在しないか、オーナーがログアウトして消去された可能性があります。
               </BasicH2>
           </div>
       );
   }
   if (!user) {
       return (
           <div>
               <BasicHead />
               <div className={ Styles.title }>
                   <TitleLogo />
               </div>
               <div className={ Styles.container }>
                   <BasicH2>どうやらあなたはログインが済んでいないようです。ここから匿名ログインをしてください</BasicH2>
                   <BasicButton
                       fullWidth ={ true }
                       onclick   ={ () => router.push("/chatroom/[roomid]/signin", location.pathname + "/signin") }
                   >匿名ログイン</BasicButton>
               </div>
           </div>
       );
   }
   return (
       <div>
           <BasicHead />
           <main>
               <div className={ Styles.title }>
                   <TitleLogo />
               </div>
               <div className={ Styles.toProfile }>
                   <BasicButton
                       onclick={ () => router.push("/profile/[username]", "/profile/" + user.displayName) }
                   >
                       プロフィールページへ
                   </BasicButton>
               </div>
               <div className={ Styles.area }>
                   { chatArea() }
               </div>
               <div className={ Styles.userField }>
                   <UserField
                       user={ user }
                   />
               </div>
           </main>
       </div>
   );
}

前回ある程度説明していたと思います。今回追加したのはchatArea関数だと思うのでそこを紹介します。この関数は指定のchatroomをfirestoreから読み込みます。指定のchatroomがあればChatContainerコンポーネントを、なければその旨を伝えるメッセージを表示します。それではChatContainerについて説明します。

ChatContainer.tsx

import { FC } from "react";
import { ChatType } from "../../functions/database";
import ChatBalloon from "./chatballoon";
import { makeStyles, createStyles } from "@material-ui/core";
const useStyles = makeStyles(() => createStyles({
   container: {
       
   }
}))
type Props = {
   chats: ChatType[],
   user:  string,
}
/**
* 一つ一つの書き込みを順番に表示する領域
* @param param0 
*/
const ChatContainer:FC<Props> = ({ chats, user }) => {
   const classes = useStyles();
   return (
       <div className={ classes.container }>
           { chats.map(chat => {
               if (chat.uid === user) {
                   return (
                       <ChatBalloon 
                           chat ={ chat }
                           who  ={ "me" }
                       />
                   );
               } else {
                   return (
                       <ChatBalloon
                           chat ={ chat }
                           who  ={ "you" }
                       />
                   );
               }
           })}
       </div>
   );
}
export default ChatContainer;

これは親のコンポーネントからroomの中のchatsとuserの情報を受け取りチャットのメッセージを表示する領域のコンポーネントです。
普通チャットと言えば自分のメッセージが右側、自分以外のメッセージが左側に表示されます。なので読み込んだchatプロパティのuidがuserのuidと一致するか動画でChatBalloonコンポーネントのwhoを"me"と "you"に分けて渡します。

ChatBalloon.tsx

import { FC } from "react";
import ContainerDiv from "../atom/containerDiv";
import BasicParagraph from "../atom/basicP";
import UserIcon from "../atom/userIcon";
import { ChatType } from "../../functions/database";
import { makeStyles, createStyles } from "@material-ui/core";
const useStyles = makeStyles(() => createStyles({
   container: {
       display: "inline-block",
       marginTop: "1rem",
       marginBottom: "1rem",
       width: "97%",
       height: "auto",
       boxSizing: "border-box",
   },
   pContainer: {
       width: "calc(100% - 100px)",
       float: "left",
       boxSizing: "border-box",
   },
   userArea: {
       width: "100px",
       float: "left",
       textAlign: "center",
   },
   date: {
       marginTop: "10px",
       float: "right",
   },
}));
type Props = {
   chat: ChatType,
   who: "me" | "you",
}
/**
* ユーザアイコン、チャットの内容を表示するコンポーネント
* @param param0 
*/
const ChatBalloon:FC<Props> = ({
   chat, who
}) => {
   const classes = useStyles();
   const date    = new Date(chat.date);
   if (who === "you") {
       return (
           <div className={ classes.container }>
           <div className={ classes.userArea }>
               <UserIcon
                   image={ chat.photoURL }
                   alt  ={ chat.username }
                   width={ 70 }
                   height={ 70 }
               />
               { chat.username }
           </div>
           <div className={ classes.pContainer }>
               <ContainerDiv
                   fullwidth={ true }
               >
                   <BasicParagraph>
                       { chat.text }
                   </BasicParagraph>
               </ContainerDiv>
               <div className={ classes.date }>
                   <BasicParagraph>
                       { date.toLocaleString() }
                   </BasicParagraph>
               </div>
           </div>
       </div>
       );
   }
   return (
       <div className={ classes.container }>
           <div className={ classes.pContainer }>
               <ContainerDiv
                   fullwidth={ true }
               >
                   <BasicParagraph>
                       { chat.text }
                   </BasicParagraph>
               </ContainerDiv>
               <div className={ classes.date }>
                   <BasicParagraph>
                       { date.toLocaleString() }
                   </BasicParagraph>
               </div>
           </div>
           <div className={ classes.userArea }>
               <UserIcon
                   image={ chat.photoURL }
                   alt  ={ chat.username }
                   width={ 70 }
                   height={ 70 }
               />
               { chat.username }
           </div>
       </div>
   );
}
export default ChatBalloon;

これはchatとwho情報を受け取って、よくあるメッセージ・ユーザアイコン・ユーザネームを表示する領域です。

これを実行するとこのようになります。

スクリーンショット 2020-08-23 16.34.00

見た目は良さそうですね。次回は書き込むフォームを作っていこうと思います。

この記事が気に入ったらサポートをしてみませんか?