チャットApp(チャットルーム作成機能)

これまでチャットAppについての記事をある程度の量出してきたのですが、見てくださる人が複数人いて中には「スキ」してくれる方もいらっしゃり本当に嬉しいです。
日記的なノリで書いているので解りやすい記事にはなっていませんし結構な量をgithubのリポジトリを見てくださいと読者の自主性に投げてしまっていますが、作っていて気づいたことなどはしっかり残していこうと思います。この記事の内容が少しでも読んでくれているみなさんの参考になればと思います。

今日は、ユーザのチャットルームを作成する機能を作ります。firebaseはnoSQLなので、json形式、もっというとJavaScriptで扱うオブジェクト形式をそのままの形で書き込み・読み込みが出来ます。なのでデータベースの構造としては、以下のようになります。

スクリーンショット 2020-08-20 13.10.31

usernameは個人が自由につける名前なのでusernameとuidでチャットルームを作成します。usernameをいれる理由は検索の時に使用するからです。

では実際にデータベース(firestore)に保存する関数を書いていきます。functionsディレクトリの中にdatabase.tsを作成し、以下を記述します。

database.ts

import { FBdb } from "./firebase";
export type ChatType = {
   text:     string,
   date:     string,
   username: string,
   photoURL: string,
}
export type ChatroomType = {
   owner:  string,
   member: string[],
   chats:  ChatType[],
}
/**
* firestoreにチャットルームを保存する。
* 保存の成功・失敗ごとにメッセージを返す。
* @param chatroom :chatroomオブジェクトを指定
*/
export const setChatroomToFirestore = async(chatroom:ChatroomType):Promise<string> => {
   const doc:string = chatroom.owner;
   const msg:string = await FBdb.collection("chatrooms").doc(doc).set({
       owner:  chatroom.owner,
       member: chatroom.member,
       chats:  chatroom.chats,
   })
   .then(() => {
       return "set chatroom successfully!";
   })
   .catch(() => {
       return "set chatroom failed.";
   });
   return msg;
}
/**
* firestoreから指定されたchatroomオブジェクトを取ってくる。
* 成功したらオブジェクトを返す。
* 保存の成功・失敗ごとにメッセージを返す。
* @param doc :username+uidを指定
*/
export const getChatroomFromFirestore = async(doc:string):Promise<[string, any]> => {
   let chatroom = {};
   const msg:string = await FBdb.collection("chatrooms").doc(doc).get()
   .then(result => {
       chatroom = {
           owner:  result.data().owner,
           member: result.data().member,
           chats:  result.data().chats,
       }
       return "get chatroom successfully!";
   })
   .catch(error => {
       return "get chatroom failed.";
   });
   return [msg, chatroom];
}

関数の説明はコードに書いている注釈の通りです。これを前回までに作成していた[username].tsxで読み込んで変更していきます。

[username].tsx

import { ChatroomType, setChatroomToFirestore, getChatroomFromFirestore } from "../../functions/database";
const [roomExist, setRoomExist] = useState(false); //チャットルームの有無で真偽を返す

// firestoreからチャットルームを読み込む関数
const getChatroom = async() => {
   if (await activeUserExist()) {
       // 最初に読み込むのでuser stateからだと間に合わないかも
       const usr = getActiveUser();
       const owner:string = usr.displayName + ":" + usr.uid;
       const [msg, temp] = await getChatroomFromFirestore(owner);
       if (msg === "get chatroom successfully!") {
           // ボタンを表示するcssクラスを付与
           console.log(msg);
           setRoomExist(true);
       } else {
           setRoomExist(false);
       }
   }
}

// 空のチャットルームを作成しfirestoreに書き込む
// チャットルームの初期化にも使用する
const createChatroom = async() => {
   const owner:string = user.displayName + ":" + user.uid;
   const chatroom:ChatroomType = {
       owner:  owner,
       member: [],
       chats:  [],
   }
   const msg = await setChatroomToFirestore(chatroom);
   console.log(msg);
   const [getmsg, temp] = await getChatroomFromFirestore(owner);
   if (getmsg === "get chatroom successfully!") {
       // ボタンを表示するcssクラスを付与
       setRoomExist(true);
   } else {
       setRoomExist(false);
   }
}

// firestoreからチャットルームを読み込めたかどうかで異なるボタンを作成する。
const createRoomBtn = () => {
   if (!roomExist) {
       return (
           <BasicButton
               fullWidth ={ true }
               onclick   ={ createChatroom }
           >
               チャットルーム作成
           </BasicButton>
       );
   }
   return (
       <BasicButton
           fullWidth ={ true }
           onclick   ={ createChatroom }
       >
           チャットルーム初期化
       </BasicButton>
   );
}

useEffect(() => {
   (async() => {
       await getChatroom();
   })();
   getUserData();
   console.log("set user, set room");
}, [setUser, setRoomExist]);


前回から変更・追加をした部分を抜粋しています。getChatroomをuseEffectでページを開いた時に実行することでチャットルームを作成していればroomExistをtrueにします。
createRoomBtn関数はroomExistによって「チャットルーム作成」と書かれたボタンか「チャットルーム初期化」と書かれたボタンを作成します。roomExistがfalseの場合はまだチャットルームが作成されていないので「チャットルーム作成」が、trueの場合はすでにfirestoreに存在しているので「チャットルーム初期化」が表示されます。
上で作成されるボタンにはcreateChatroom関数が付与されます。これは空のチャットルームを作成し、firestoreに書き込みます。その後作成したチャットルームを読み込みます。
あとはこれに合わせてreturnのcontainerDiv内の記述も以下のように変更します。

<ContainerDiv>
   { createRoomBtn() }
   <div className={ Styles.span }></div>
   <BasicButton
       fullWidth ={ true }
       disabled  ={ !roomExist }
   >
       自分のチャットルームへ
   </BasicButton>
   <div className={ Styles.search }>
       <BasicParagraph>
           他の人のチャットルームを検索する場合は以下の検索ボックスを使ってください。
       </BasicParagraph>
       <SearchBox />
   </div>
</ContainerDiv>

これを実行すると下のようになります。最初はチャットルームは作っていない状態なので「チャットルーム作成」ボタンになります。

スクリーンショット 2020-08-20 13.39.35

チャットルーム作成ボタンを押すと

スクリーンショット 2020-08-20 13.39.48

このように「チャットルーム初期化」に変化し、自分のチャットルームへボタンが押せるようになります。firestoreにもデータが保存されています。

スクリーンショット 2020-08-20 13.40.23

これでチャットルームが作成できるようになったので次はチャットルームページのビュー部分を作成します。そのあと自分以外のチャットルームを検索する機能を実装します。

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