はじめに
expoでメッセージ機能がついたアプリを作る際、通知機能は必須ということで、Cloud Functions for Firebaseを使って通知機能を実装します。
プッシュトークンを取得する
↑を参考にユーザから通知の許可を取り、プッシュトークンを取得します。
index.tsx
const registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const {
status: existingStatus
} = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
//alert("プッシュ通知のプッシュトークンを取得できませんでした.");
return;
}
//const token = (await Notifications.getExpoPushTokenAsync()).data;
//console.log(token);
} else {
//alert("プッシュ通知には物理デバイスを使用する必要があります");
return;
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C"
});
}
};
プッシュトークンを保存する
今回は複数端末に通知を送ることができるように、以下のようにプッシュトークンをCloud Firestoreに保存します。
ログイン時やログアウト時にプッシュトークンを追加・削除するようにしています。
db.ts
export const db = firebase.firestore();
export const addPushToken = async (uid: string) => {
if (Constants.isDevice) {
const pushToken = await Notifications.getExpoPushTokenAsync();
await db.collection("users").doc(uid).update({
pushToken: firebase.firestore.FieldValue.arrayUnion(pushToken.data)
});
}
};
export const removePushToken = async (uid: string) => {
if (Constants.isDevice) {
const pushToken = await Notifications.getExpoPushTokenAsync();
await db.collection("users").doc(uid).update({
pushToken: firebase.firestore.FieldValue.arrayRemove(pushToken.data)
});
}
};
メッセージ機能の実装
↑を使い、メッセージページを作りました。
onSend
にて以下をFirestoreに保存します。
{
id,
uid, // 送信者のuid
friendId, // 受信者のuid
createdAt,
text // メッセージ内容
}
Cloud Functionsの実装
新しいメッセージが保存されることをトリガーにしています。
↑を参考にしています。
メッセージ送信にて保存した受信者のidからpush tokenを取得し、送信者のidから送信者名を取得し、通知を送信します。
index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const { Expo } = require("expo-server-sdk");
const expo = new Expo();
exports.sendNotification = functions
.region("asia-northeast1")
.firestore.document("messages/{messageId}")
.onCreate(async (snap, context) => {
const messageData = snap.data();
// 受信者のpush tokenの取得
const recipientId = messageData.friendId;
const recipientSnapshot = await db
.collection("users")
.doc(recipientId)
.get();
const recipientData = recipientSnapshot.data();
if (!userData.pushToken) {
functions.logger.log("push tokenを取得できませんでした。");
} else {
const pushTokenList = userData.pushToken;
// 送信者の名前の取得
const senderId = messageData.uid;
const senderSnapshot = await db
.collection("users")
.doc(senderId)
.get();
const senderData = senderSnapshot.data();
const senderName = senderData.name;
// プッシュ通知用のメッセージオブジェクトを作成
let messages = [];
for (let pushToken of pushTokenList) {
if (!Expo.isExpoPushToken(pushToken)) {
functions.logger.error(
`Push token ${pushToken} is not a valid Expo push token`
);
}
messages.push({
to: pushToken,
title: senderName,
body: messageData.text
});
}
const chunks = expo.chunkPushNotifications(messages);
const tickets = [];
(async () => {
// Expo Push API をリクエスト
for (let chunk of chunks) {
try {
const ticketChunk = await expo.sendPushNotificationsAsync(chunk);
tickets.push(...ticketChunk);
} catch (error) {
functions.logger.error(error);
}
}
})();
}
});