チャットApp(認証の変更)
前回言った通り認証を匿名認証のみにし、ログアウトしたら関係情報を全て消去するようにしようと思います。
不必要なコードの削除
まずは不必要なものを消していきます。消したのは
signup.tsx
signup.module.css
書き換えたのがsignin.tsxを以下のように書き換えました。
signin.tsx
import { useState } from "react";
import { useRouter } from "next/router";
import BasicHead from "../components/atom/head";
import TitleLogo from "../components/atom/logo";
import BasicH2 from "../components/atom/basicH2";
import BasicParagraph from "../components/atom/basicP";
import BasicButton from "../components/atom/button";
import ContainerDiv from "../components/atom/containerDiv";
import basicData from "../components/atom/basicData";
import Styles from "../styles/signin.module.css";
import { signinAnonymous } from "../functions/auth";
export default function SignIn() {
const router = useRouter();
const [ disable, setDisable ] = useState(false);
const handleSigninAnonymous = async() => {
setDisable(true);
const bool:boolean = await signinAnonymous();
if (bool) {
console.log("successfully!");
router.push("/profile/setting");
} else {
console.log("failed.");
}
setDisable(false);
}
return (
<div>
<BasicHead />
<main>
<div className={ Styles.title }>
<TitleLogo />
</div>
<div className={ Styles.box }>
<ContainerDiv>
<div className={ Styles.register }>
<BasicH2>ログイン</BasicH2>
</div>
<div className={ Styles.paragraph }>
<BasicParagraph>
{ basicData.signin }
</BasicParagraph>
</div>
<div className={ Styles.innerbox }>
<BasicButton
fullWidth ={ true }
disabled ={ disable }
onclick ={ handleSigninAnonymous }
>
匿名ログイン
</BasicButton>
</div>
</ContainerDiv>
</div>
</main>
</div>
);
}
signin.tsxで行っていたメール・パスワードによるログイン、google, twitterによるログインを削除しています。これらは一応確認のために実装したものなので、プロジェクトの目的上は必要ありません。結構な量のコードを書いたので名残惜しいですが、必要ないと判断したならバッサリいきましょう。
もしまた必要になってもfirebaseの関数はauth.tsに残してますし、githubの別のブランチにも残っているので気にすることはありません。
またプロフィールページも認証されていなければルートへのリンクのみが表示されるように書き換えています。
[username].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 BasicParagraph from "../../components/atom/basicP";
import ContainerDiv from "../../components/atom/containerDiv";
import SearchBox from "../../components/compo/searchBox";
import UserField from "../../components/compo/userField";
import SignoutButton from "../../components/compo/signoutButton";
import NoUser from "../../components/compo/nouser";
import Styles from "../../styles/profile.module.css";
import { getActiveUser, activeUserExist } from "../../functions/auth";
import { ChatroomType, setChatroomToFirestore, getChatroomFromFirestore } from "../../functions/database";
export default function Profile() {
const [user, setUser] = useState<firebase.User>();
const [roomExist, setRoomExist] = useState(false);
const router = useRouter();
const getUserData = async() => {
if (await activeUserExist()) {
const temp = getActiveUser();
setUser(temp);
}
}
const getChatroom = async() => {
if (await activeUserExist()) {
// 最初に読み込むのでuser stateからだと間に合わないかも
const usr = getActiveUser();
const [msg, temp] = await getChatroomFromFirestore(usr.uid);
if (msg === "get chatroom successfully!") {
// ボタンを表示するcssクラスを付与
console.log(msg);
setRoomExist(true);
} else {
setRoomExist(false);
}
}
}
const createChatroom = async() => {
const owner:string = user.displayName;
const chatroom:ChatroomType = {
owner: owner,
roomname: "",
member: [],
chats: [],
}
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);
}
}
const createRoomBtn = () => {
if (!roomExist) {
return (
<BasicButton
fullWidth ={ true }
onclick ={ createChatroom }
>
チャットルーム作成
</BasicButton>
);
}
return (
<BasicButton
fullWidth ={ true }
onclick ={ createChatroom }
>
チャットルーム初期化
</BasicButton>
);
}
useEffect(() => {
(async() => {
await getChatroom();
await getUserData();
})();
}, [setUser, setRoomExist]);
if (user) {
return (
<div>
<BasicHead />
<main>
<div className={ Styles.title }>
<TitleLogo />
</div>
<div className={ Styles.maincontainer }>
<ContainerDiv>
{ createRoomBtn() }
<div className={ Styles.span }></div>
<BasicButton
fullWidth ={ true }
disabled ={ !roomExist }
onclick ={() => router.push("/chatroom/[roomid]", `/chatroom/${user.uid}`) }
>
自分のチャットルームへ
</BasicButton>
<div className={ Styles.search }>
<BasicParagraph>
他の人のチャットルームを検索する場合は以下の検索ボックスを使ってください。
</BasicParagraph>
<SearchBox />
</div>
</ContainerDiv>
</div>
<UserField
user={ user }
/>
</main>
<SignoutButton />
</div>
);
}
return (
<div className={ Styles.nouser }>
<NoUser />
</div>
);
}
ログアウト機能の実装
不必要なものは削除したので、ログアウト機能を実装していきましょう。ボタンとして実装します。今のところプロフィールページだけに置こうと思っていますが、後々のことを考えてコンポーネントとして実装します。
components/compo/signout.tsx
import { FC } from "react";
import { useRouter } from "next/router";
import BasicButton from "../atom/button";
import { makeStyles, createStyles } from "@material-ui/core";
import { getActiveUser } from "../../functions/auth";
import { deleteFirestoreChatroom } from "../../functions/database";
import { deleteUserImage } from "../../functions/storage";
const useStyles = makeStyles(() => createStyles({
pos: {
position: "fixed",
top: "10px",
right: "10px",
zIndex: 10000,
}
}))
const SignoutButton:FC = () => {
const classes = useStyles();
const router = useRouter();
const slicePhotoURL = (photoURL:string) => {
const split1 = photoURL.split("/userPhoto%2F");
const split2 = split1[1].split("?alt");
const imagePath = "userPhoto/" + split2[0];
// 正規表現で該当する全ての文字を置き換える。
const replaced = imagePath.replace(/%20/g, ' ');
return replaced;
}
const handleSignout = async() => {
const result:boolean = window.confirm("ログアウトするとこのアカウントに関わる情報は削除されます。よろしいですか?");
if (result) {
const user = getActiveUser();
// チャットルームの削除
const chatroomb = await deleteFirestoreChatroom(user.uid);
// ユーザアイコンの削除
const photoURL = user.photoURL;
if (photoURL) {
const imagePath = slicePhotoURL(photoURL);
console.log(imagePath);
const userImageb = await deleteUserImage(imagePath);
}
// ユーザアカウントの削除
user.delete()
.then(() => {
router.push("/");
})
.catch(error => {
console.log(error);
router.push("/");
})
}
}
return (
<div className={ classes.pos }>
<BasicButton
onclick={ handleSignout }
>
ログアウト
</BasicButton>
</div>
);
}
export default SignoutButton;
ここで重要になるのは、handleSignout関数です。やっていることは
サインアウト実行の確認
ユーザのチャットルームの削除
ユーザのアイコンの削除
ユーザのアカウントの削除
ルートページに移動
という感じです。ユーザアイコンに関しては、photoURLからstorageに保存しているファイル名を抜き取る必要があるのでslicePhotoURLという関数を作成し、そこで行っています。少し説明すると、ユーザのアイコンに関してはstorageの/userPhotoディレクトリの中に保存しているので、それより前の部分をsliceメソッドで切ります。また後半にはTokenIDのようなものが「?alt」以降にひっついているのでそれもsliceして切ります。あとスペース文字は%20になっているのでそれをreplaceでスペースに置換したものを返り値として返します。これでもとのファイル名が取り出せます。
このサインアウトボタンを[username].tsxで読み込んだら実行して確認します。
匿名ログインをしてusernameとアイコンを設定します。
ログアアウトをするとfirebaseの方にあったデータが全て消去されているのでOKです。
これで、ログインしてからログアウトするまでの間だけアカウントが有効になります。
さて、これでURLを教えて招待することも可能ですが、前回言ったように招待した人が認証されていないことがありえます。
次回はURLで直接リンクしてきたユーザが認証されていなかった場合、認証ページにリダイレクトさせて、チャットページ行くよう実装していきたいと思います。
この記事が気に入ったらサポートをしてみませんか?