RN(expo)でNotification


ひさいぶりにexpoのNotificationを調べたら、前のコードでは動かなくなっていたので再度メモ。
ついでに、送信をCloud functionsから行ってみる。

## 以前との変更

* Permissionsがexpoから切離され、expo-permissionsを別途インストール、importする必要がある。

## 注意点（私の環境に依存）

* firebase関連の設定を外部の./config/Firebase.jsで行っている（firebaseとdbを設定してるだけ）。
* 既にfirestoreへ接続できる状態になっている。


## 実装：ReactNative側

特に難しいことはない。


```js:App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { Notifications } from 'expo';
import * as Permissions from 'expo-permissions'; //呼び出し方変わってる
import Firebase, { db } from './config/Firebase';
import firebase from 'firebase';

export default class App extends React.Component {

    registerForPushNotificationsAsync = async () => {

        try {
            //現在のNotificationパーミッション状況取得
            const { status: existingStatus } = await Permissions.getAsync(
                Permissions.NOTIFICATIONS
            );
            let finalStatus = existingStatus;

            //statusが許可じゃなければ（許可済みなら何もしない）
            if (existingStatus !== 'granted') {
                //許可を尋ねる
                const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
                finalStatus = status;
            }

            //それでも許可されてなかったら何もしない
            if (finalStatus !== 'granted') {
                return;
            }

            //token取得
            const token = await Notifications.getExpoPushTokenAsync();

            //保存
            await this.saveDeviceToken(token);

            //表示
            alert("token=" + token);
            console.log(token);

        } catch (e) {
            console.log(e);
        }
    }

    saveDeviceToken = async (token) => {
        try {
            const docRef = await db.collection('tokens').add({
                token: token,
                created_at: firebase.firestore.FieldValue.serverTimestamp(),
                user: 'sample',
            })
        } catch (e) {
            console.log(e);
        }
    }

    handleNotification = (notification) => {
        if (notification.origin === 'selected') {
            //何もしなくても通知される
        } else if (notification.origin === 'received') {
            //jsonで{"message":"ほげほげ"}が送られていることが前提なら、下記のように書ける。
            //alert('通知が来たようです。内容は:' + notification.data.message + "です。");
            alert('通知が来たようです。');
        }
    }

    componentDidMount = () => {
        //リスナー登録
        this.notificationSubscription = Notifications.addListener(this.handleNotification);
    }

    componentWillUnmount = () => {
        //これを入れないと、起動時に通知が来る
        this.notificationSubscription.remove();
    }

    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <Text>App</Text>
                <Button
                    title="get Notification token"
                    onPress={this.registerForPushNotificationsAsync}
                />
            </View>
        );
    }
}

```

## 実装：Cloud Functions側

とりあえず、ピンポイントにdocumentを指定してtokenを取得し、そのtokenを利用して（一人）にNotificationを送信している。


```js:index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

///expo
const { Expo } = require('expo-server-sdk');
const expo = new Expo();



exports.helloExpo = functions.region('asia-northeast1').https.onRequest(async (request, response) => {

    //とりあえずdocを指定してtokenを取得
    const docRef = db.collection('tokens').doc('omMCYVMGnzcxIySy2DSo');
    const snapshot = await docRef.get().catch(e => console.log(e));
    const token = snapshot.data().token;

    //メッセージ組み立て
    const message = {
        to: token,
        sound: 'default',
        title: 'test',
        body: 'hello',
        data: { name: 'foo', message: 'test' }
    }

    //メッセージをまとめる
    const chunks = expo.chunkPushNotifications([message]);
    const tickets = [];

    //ループ
    chunks.forEach(async chunk => {
        //送る
        const ticketChunk = await expo.sendPushNotificationsAsync(chunk).catch(e => console.log(e));
        tickets.push(...ticketChunk);
    });

    response.send('send notificaiton.')
});
```

## 課題

* フォワグラウンドのときにNotificationが届いたときの処理についてもう少し調べる必要がある。