はじめに
この記事は、こちらの記事の続きとなります!
追加した機能
- Firebaseの認証機能を使ってログイン機能を追加
- ログイン済みのユーザーはマイページにアクセスでき、最近の成績、平均点などを見られる
実装したソースコード
store/index.js
ここでは、ユーザーの情報と、ローディング中の状態管理をしています。
import firebase from '~/plugins/firebase'
export const state = () => ({
loginUser: null,
isLoading: false,
})
export const getters = {
loginUser: (state) => state.loginUser,
isLoading: (state) => state.isLoading,
}
export const mutations = {
setLoginUser(state, user) {
// userから必要なものだけを抜き出します
const { uid, email, displayName, photoURL } = user
state.loginUser = { uid, email, displayName, photoURL }
},
deleteLoginUser(state) {
// ログアウト後はstateからloginUserを削除します
state.loginUser = null
},
stopLoading(state) {
state.isLoading = true
},
}
export const actions = {
setLoginUser({ commit }, user) {
commit('setLoginUser', user)
},
deleteLoginUser({ commit }) {
commit('deleteLoginUser')
},
login() {
// google認証
const googleAuthProvider = new firebase.auth.GoogleAuthProvider()
firebase.auth().signInWithRedirect(googleAuthProvider)
},
logout({ commit }) {
firebase
.auth()
.signOut()
.then(() => {
commit('deleteLoginUser')
this.$router.push('/')
})
},
stopLoading({ commit }) {
commit('stopLoading')
},
}
store/result.js
ここでは、クイズの成績を管理します
import firebase from '~/plugins/firebase'
export const state = () => ({
results: [],
})
export const getters = {
results: (state) => state.results,
countTypeOfQuiz: (state) => {
const res = state.results.reduce(
(acc, el) => {
if (el.typeOfQuiz === 'flag') acc.flagCount++
if (el.typeOfQuiz === 'region') acc.regionCount++
return acc
},
{ regionCount: 0, flagCount: 0 }
)
return res
},
averageOfCorrectAnswers: (state) => {
const res = state.results.reduce((acc, el) => {
acc += el.correctAnswerCount
return acc
}, 0)
if (res) return (res / state.results.length).toFixed(2)
else return 0
},
}
export const mutations = {
addResult(state, result) {
// ascで古い順にcommitされていく & 一時的にstateに入れるものは最新なのでunshiftを使う
state.results.unshift(result)
},
}
export const actions = {
fetchResult({ commit }, uid) {
return new Promise(function (resolve) {
firebase
.firestore()
.collection(`users/${uid}/results`)
.orderBy('createdAt', 'asc')
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
commit('addResult', doc.data())
})
})
.then(() => {
resolve()
})
})
},
addResult({ commit }, { uid, typeOfQuiz, correctAnswers }) {
if (uid) {
const targetDate = new Date()
const resultObj = {
typeOfQuiz,
correctAnswerCount: correctAnswers,
createdAt: targetDate,
}
firebase.firestore().collection(`users/${uid}/results`).add(resultObj)
commit('addResult', resultObj)
}
},
}
default.vue
<template>
<div id="container">
<Nuxt v-if="isLoading" />
<loading v-else>loading</loading>
</div>
</template>
<script>
import firebase from 'firebase'
import { mapGetters, mapActions } from 'vuex'
import Loading from '@/components/Loading'
export default {
components: {
Loading,
},
computed: {
...mapGetters(['loginUser']),
...mapGetters(['isLoading']),
},
created() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const that = this
new Promise(function (resolve) {
that.setLoginUser(user)
resolve()
})
.then(function () {
return new Promise(function (resolve) {
that.fetchResult(that.loginUser.uid).then(() => {
resolve()
})
})
})
.then(function () {
that.stopLoading()
})
} else {
this.deleteLoginUser()
this.stopLoading()
}
})
},
methods: {
...mapActions(['setLoginUser', 'deleteLoginUser', 'stopLoading']),
...mapActions('result', ['fetchResult']),
},
}
</script>
pages/mypage.vue
<template>
<div id="mypage">
<div class="nav">
<router-link to="/">TOP</router-link>
<a class="logout" @click.prevent="logout">ログアウト</a>
</div>
<div class="mypage-inner">
<h1 class="title is-1">COUNTRY QUIZ</h1>
<div class="mypage-box">
<div class="user-info">
<div v-if="loginUser">
<img :src="loginUser.photoURL" alt="" />
<p>{{ loginUser.displayName }}</p>
</div>
</div>
<div class="results">
<table class="table is-striped is-fullwidth">
<thead>
<tr>
<th>日付</th>
<th>挑戦したクイズ</th>
<th>結果</th>
</tr>
</thead>
<tbody>
<tr v-for="result in results" :key="result.nanoseconds">
<td>{{ result.createdAt | convertCreatedAt }}</td>
<td>{{ result.typeOfQuiz | convertTypeOfQuiz }}</td>
<td>{{ result.correctAnswerCount }}/5</td>
</tr>
</tbody>
</table>
</div>
<div class="graf-box">
<div class="pie">
<pie-chart :count-type-of-quiz="countTypeOfQuiz"></pie-chart>
</div>
<div class="average">
<h4>平均</h4>
<p>{{ averageOfCorrectAnswers }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import moment from 'moment'
import PieChart from '@/components/PieChart'
export default {
components: {
PieChart,
},
data() {
return {
typeOfQuizObj: null,
}
},
computed: {
...mapGetters(['loginUser']),
...mapGetters('result', [
'results',
'countTypeOfQuiz',
'averageOfCorrectAnswers',
]),
},
created() {
if (!this.loginUser) {
this.$router.push('/')
}
},
methods: {
...mapActions(['logout']),
...mapActions('result', ['fetchResult', 'addResult']),
},
filters: {
convertCreatedAt(val) {
if (val.seconds) {
const dateTime = moment.unix(val.seconds).format('YYYY/M/D')
return dateTime
} else {
const dateTime = moment(val.seconds).format('YYYY/M/D')
return dateTime
}
},
convertTypeOfQuiz(val) {
if (val === 'region') return '地域区分クイズ'
else if (val === 'flag') return '国旗クイズ'
else return 'error'
},
},
}
</script>