チャットApp(仕様変更とprofileページ)

今回は、前回のfirebaseの仕様に伴う修正でわかってきたfirebaseの仕組みを踏まえてプロジェクトの仕様を変更し、profileページのビュー部分を整えようと思います。

仕様変更

私が勘違いしていたようですが、firebaseの匿名認証はどうやら端末(もしくはブラウザ?)で識別しているようでログアウトした後も同じ端末(ブラウザ)から匿名ログインすると前にログインしたアカウントでログインする形になるようです。
そうなると「ちゃちゃっと」というタイトルの通り匿名認証で簡単にチャットをした方が楽なのでもう少し匿名認証をフィーチャーした形で作ろうかなと思います。
また、当初の予定では、一人が複数のチャットルームを作れるように考えていたのですが、まずは一つ作れればいいだろうということで下のように変更しました。

スクリーンショット 2020-08-19 17.25.56

チャットルームの検索ページを削除し、プロフィールページの中で検索できるようにしました。

profileページの作成

前回はプロフィールページにユーザデータを読み込むことに時間を割いてしまったので今回こそはビューの部分を整えます。まずユーザのアイコンを表示するコンポーネントとアイコンとユーザ名をセットにしたコンポーネントを作ります。これはチャットページでも利用するのでコンポーネントとして作っています。

userIcon.tsx

import { FC } from "react";
import { makeStyles, createStyles } from "@material-ui/core";
const useStyles = makeStyles(() => createStyles({
   image: {
       width: "100px",
       height: "100px",
       borderRadius: "50%",
   },
   container: {
       display: "block",
   }
}));
type imageType = {
   image: string,
   alt:string,
   width?: number,
   height?:number,
}
const UserIcon:FC<imageType> = ({
   image, alt, width, height,
}) => {
   const classes = useStyles();
   return (
       <div className={ classes.container }>
           <img 
               src={ image || "/images/default-user-image.png" } 
               alt={ alt}
               className={ classes.image }
               style={{ width: width, height: height}}
           />
       </div>
   );
}
export default UserIcon;

userField.tsx

import { FC } from "react";
import UserIcon from "../atom/userIcon";
import BasicH2 from "../atom/basicH2";
import { createStyles, makeStyles } from "@material-ui/core";
const useStyles = makeStyles(() => createStyles({
   container: {
       position: "absolute",
       bottom: "10px",
       right: "10px",
       textAlign: "center",
       zIndex: 10000,
   },
   username: {
       textAlign: "center",
   }
}));
type UF = {
   user:    firebase.User,
   width?:  number,
   height?: number,
}
const UserField:FC<UF> = ({
   user, width, height,
}) => {
   const classes = useStyles();
   return (
       <div className={ classes.container }>
           <div >
               <UserIcon
                   image  ={ user?.photoURL }
                   alt    ="user icon"
                   width  ={ width }
                   height ={ height }
                   />
           </div>
           <div className={ classes.username }>
               <BasicH2>
                   { user?.displayName || "no user" }
               </BasicH2>
           </div>
       </div>
   );
}
export default UserField;


特に変わったことはしていま1000。続いてチャットルームを検索するサーチボックスのコンポーネントを作成します。

searchBox.tsx

import { FC, useState } from "react";
import BasicTextField from "../atom/textbox";
import BasicButton from "../atom/button";
import { makeStyles, createStyles } from "@material-ui/core";
import { themeColor } from "../atom/styles";
const useStyles = makeStyles(() => createStyles({
   container: {
       width: "100%",
   },
   textfield: {
       position: "relative",
       display: "inline-block",
       boxSizing: "border-box",
       width: "70%",
   },
   btnBox: {
       position: "relative",
       display: "inline-block",
       width: "30%",
       textAlign: "center",
       top: "18px",
   }
}));
const SearchBox:FC = () => {
   const classes = useStyles();
   const [word, setWord] = useState("");
   const handleChangeWord = (event:React.ChangeEvent<{ value: unknown }>) => {
       setWord(event.target.value as string);
   }
   // 検索ワードを元に部分一致するチャットルームをfirebaseから検索して表示する関数
   const searchRoomFromFirebase = () => {
       alert("searching");
   }
   return (
       <div className={ classes.container }>
           <div className={classes.textfield }>
               <BasicTextField
                   label="チャットルームを検索"
                   value={ word }
                   onchange={ handleChangeWord }
                   fullWidth={ true }
               />
           </div>
           <div className={ classes.btnBox }>
               <div>
                   <BasicButton
                       onclick={ searchRoomFromFirebase }
                   >
                       検索
                   </BasicButton>
               </div>
           </div>
       </div>
   );
}
export default SearchBox;


チャットルームを検索する関数は後から実装します。あとはこれらを[username].tsxで下のように読み込んであげればOKです。

import { useState, useEffect } from "react";
import BasicHead from "../../components/atom/head";
import TitleLogo from "../../components/atom/logo";
import BasicButton from "../../components/atom/button";
import BasicParagraph from "../../components/atom/basicP";
import ContainerDiv from "../../components/atom/containerDiv";
import SearchBox from "../../components/compo/searchBox";
import UserField from "../../components/compo/userField";
import Styles from "../../styles/profile.module.css";
import { getActiveUser, activeUserExist } from "../../functions/auth";
export default function Profile() {
   const [user, setUser] = useState<firebase.User>();
   const getUserData = async() => {
       if (await activeUserExist()) {
           const temp = getActiveUser();
           setUser(temp);
       }
   }
   useEffect(() => {
       getUserData();
   }, [getUserData, setUser]);

   return (
       <div>
           <BasicHead />
           <main>
               <div className={ Styles.title }>
                   <TitleLogo />
               </div>
               <div className={ Styles.maincontainer }>
                   <ContainerDiv>
                           <BasicButton
                               fullWidth={ true }
                           >
                               チャットルームを作成
                           </BasicButton>
                           <div className={ Styles.search }>
                               <BasicParagraph>
                                   他の人のチャットルームを検索する場合は以下の検索ボックスを使ってください。
                               </BasicParagraph>
                               <SearchBox />
                           </div>
                   </ContainerDiv>
               </div>
               <UserField 
                   user={ user }
               />
           </main>
       </div>
   );
}


これを実行してログイン処理をすると以下のようになります。

スクリーンショット 2020-08-19 12.23.23

また同じ端末からログインすると匿名認証でも前回のuser IDが使用されるようなので、毎回ユーザネームとアイコンを設定するのは面倒なので前回までで実装したsetting.tsxでユーザネームが設定されていなければ自動的にプロフィールページに飛ぶように以下を書き加えます。

const checkUserdata = async() => {
   const user = await getActiveUser();
   if (user.displayName) {
       router.push("[username]", `${ user.displayName }`);
   }
}
useEffect(() => {
   checkUserdata();
});

ちなみにこれまで全然確認してきませんでしたが、今の進行状況はこんな感じです。

スクリーンショット 2020-08-19 17.50.33

リストの一つ一つが同じ作業量という訳ではないですが、残すところあと少しです。

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