Skip to content

Instantly share code, notes, and snippets.

@hankkuu
Forked from geoseong/NotifService.js
Created April 25, 2019 07:46
Show Gist options
  • Save hankkuu/73bb449fc231cda8b6b788b09cb304ed to your computer and use it in GitHub Desktop.
Save hankkuu/73bb449fc231cda8b6b788b09cb304ed to your computer and use it in GitHub Desktop.
Firebase Cloud Messaging via AWS Lambda
/**
shared util on React-Native src
*/
import {
PushNotificationIOS,
Platform
} from 'react-native';
import firebase from 'react-native-firebase';
import appStore from '../stores/appStore'; // mobX
import { useFetch, useAjax, requests, handleConnectivityChange } from './Networking';
import { encryptUserkey, decryptUserkey } from './Secure';
import { getSingleData, storeSingleData, storeMultiData, storageKeys } from './ControlAsyncStorage';
let notiChannelId = 0;
/**
* @name onRegister
* @param token
* @description get FCM token and send to AWS RDS server
*/
const onRegister = (token) => {
console.log('[NotifService]TOKEN:', token);
/* Store to LocalStorage */
storeSingleData(storageKeys.fcm_token, token);
/* Networking */
useFetch(requests.setDeviceInfo, 'POST', {
user_key: encryptUserkey(appStore.userPhone),
token: token,
crud: 'U',
mode: 'token',
}).then((response) => {
console.log('[NotifService]response:', response);
// if (response.status === 200) {
// let result = response.body;
// } else {
// }
}).catch((error) => {
console.log('network catch:', error);
});
}
/**
* @name onNotification
* @param {*} notification
*/
const onNotification = (notification) => {
console.log('[NotifService:onNotification]notification:', notification);
console.log('[NotifService:onNotification]appStore.projInfo:', appStore.projInfo);
if (notification.data && notification.data.PROJ_ID === appStore.projInfo.PROJ_ID) {
if (appStore.notiEnabled === 'true') {
/* Display Notification */
displayNotif(notification);
/* load TodoStatus */
useFetch(requests.getTodoStatus, 'POST', {
user_key: encryptUserkey(appStore.userPhone),
}).then((response) => {
console.log('[Notif] checkTodo response:', response);
if (response.status === 200) {
let result = response.body;
/* Modify MobX */
appStore.todoStatusCompleted = result.completed;
appStore.todoStatusIng = result.ing;
appStore.todoStatusDelay = result.delay;
} else {
/* */
}
}).catch((error) => {
console.log('[Notif] checkTodo catch:', error);
});
}
/* Assemble noti list */
let noti_item = {
...notification.data,
CONTEXT: notification.body,
read: false,
}
/* Read AsyncStorage */
getSingleData(storageKeys.noti_list).then((data) => {
console.log('[NotifService] get asyncstorage data', data);
/* Data of Async Storage */
let noti_list;
if (!data) {
noti_list = [];
} else if (data && typeof data === 'string' && data.length > 0) {
noti_list = JSON.parse(data);
} else if (data && typeof data === 'object') {
noti_list = data;
} else {
noti_list = [];
}
noti_list.splice(0, 0, noti_item);
/* Modify MobX */
appStore.notiList = noti_list;
return storeSingleData(storageKeys.noti_list, JSON.stringify(noti_list))
}).then((isSaved) => {
console.log('[NotifService] after save asyncstorage', isSaved);
console.log('[NotifService] after save asyncstorage/appStore', appStore.getNotiList());
});
} else {
console.log('[NotifService]notification.proj_id != appStore.projInfo.PROJ_ID');
}
}
const displayNotif = (notification) => {
notiChannelId++;
const {
body,
data,
notificationId,
sound,
subtitle,
title
} = notification;
console.log('[NotifService]displayNotif: ', title, body, JSON.stringify(data));
/* Displaying Notification */
const pushnotification = new firebase.notifications.Notification()
.setNotificationId(notificationId)
.setTitle(title)
.setBody(body)
.setData({
worker: data.worker_id,
project: data.proj_id,
});
if (Platform.OS === 'ios') {
pushnotification.ios.setBadge(2);
} else {
pushnotification
.android.setChannelId('reportAlarm')
.android.setSmallIcon('ic_stat_ic_notification')
.android.setVibrate([300]);
}
console.log("[NotifService]pushnotification:", pushnotification);
firebase.notifications().displayNotification(pushnotification);
};
const initFirebaseMessaging = () => {
if (Platform.OS === 'android') {
/* Create Notif Channel */
// Build a channel
const channel = new firebase.notifications.Android.Channel(
'reportAlarm',
'reportAlarm',
firebase.notifications.Android.Importance.Max
).setDescription('ProjectHUBCL');
// Create the channel
firebase.notifications().android.createChannel(channel);
}
/* Init Cloud Message */
return firebase.messaging().hasPermission().then(enabled => {
console.log('[NotifService] permission', enabled);
if (enabled) {
firebase.messaging().getToken().then((token) => {
console.log("[NotifService] getToken: ", token);
onRegister(token);
});
// user has permissions
} else {
/* disabled */
firebase.messaging().requestPermission()
.then(() => {
// alert("푸쉬알람을 받는 것을 허용하시겠습니까?");
})
.catch(error => {
// alert("Error", error)
// User has rejected permissions
});
}
});
}
const tokenRefreshListener = () => {
return firebase.messaging().onTokenRefresh(fcmToken => {
// Process your token as required
console.log("[NotifService] onTokenRefresh: ", fcmToken);
onRegister(fcmToken);
});
}
const notificationListener = () => {
return firebase.notifications().onNotification((notification: Notification) => {
console.log("[NotifService:notificationListener]notification: ", notification);
/* notification constructure
{
_android: {
_notification: [Circular],
_actions: [],
_people: [],
_smallIcon: { icon: 'ic_launcher' }
},
_ios: {
_notification: [Circular],
_alertAction: undefined,
_attachments: [],
_badge: undefined,
_category: '',
_hasAction: undefined,
_launchImage: '',
_threadIdentifier: ''
},
_body: 'myname',
_data: {
'google.c.a.ts': '1536079080',
'google.c.a.c_id': '8018338889909034580',
worker_id: 'worker',
'google.c.a.udt': '0',
'google.c.a.c_l': 'lbl',
'gcm.n.e': '1',
'google.c.a.e': '1',
proj_id: 'proj-48'
},
_notificationId: '83687E73-5408-46FC-B3B1-BBEA5C924AC7',
_sound: undefined,
_subtitle: undefined,
_title: 'parameter 나갑니다잉'
}
*/
onNotification(notification);
});
}
const initFirebaseNotiOpenedListener = () => {
/* App in Foreground and background */
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen: NotificationOpen) => {
// Get the action triggered by the notification being opened
const action = notificationOpen.action;
// Get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
console.log("[NotifService/onNotificationOpened] action:", action);
console.log("[NotifService/onNotificationOpened] notification:", notification);
});
}
const getInitialNoti = () => {
firebase.notifications().getInitialNotification()
.then((notificationOpen: NotificationOpen) => {
console.log("[NotifService/getInitialNotification] notificationOpen:", notificationOpen);
if (notificationOpen) {
// App was opened by a notification
// Get the action triggered by the notification being opened
const action = notificationOpen.action;
// Get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
}
});
}
const toggleNoti = (bool) => {
const boolStr = (bool) ? 'true' : 'false';
/* Store to MobX */
appStore.notiEnabled = boolStr;
/* Store to LocalStorage */
storeSingleData(storageKeys.notiEnabled, boolStr);
}
export {
initFirebaseMessaging,
tokenRefreshListener,
initFirebaseNotiOpenedListener,
notificationListener,
getInitialNoti,
toggleNoti,
}
/**
Firebase Push Noti를 구현한 실직적인 화면 in React-Native src
*/
// @flow
import React, { Component } from 'react';
import {
Platform,
StatusBar,
StyleSheet,
TouchableHighlight,
TouchableOpacity,
Image,
ScrollView,
Text,
View,
FlatList,
NetInfo,
Clipboard,
PushNotificationIOS,
AppState,
} from 'react-native';
import { observer, inject } from 'mobx-react/native';
import { SafeAreaView, NavigationScreenProp, NavigationStateRoute } from 'react-navigation';
import {
retrieveAllData, getSingleData, storeSingleData, storeMultiData, multiRemoveData, storageKeys, setDidMountTodo, getDidMountTodo
} from '../../utils/ControlAsyncStorage';
import { initFirebaseMessaging, initFirebaseNotiOpenedListener, notificationListener, tokenRefreshListener } from '../../utils/NotifService';
type Props = {
store: any,
navigation: NavigationScreenProp<NavigationStateRoute>,
};
type State = {
modalContext: string;
modalVisible: any;
title: string;
}
@inject('store') @observer
class Page extends Component<Props, State> {
static navigationOptions = {
header: null,
};
// notiService;
notificationListener;
initFirebaseNotiOpenedListener;
onTokenRefreshListener;
constructor(props) {
super(props);
/* Firebase Noti */
initFirebaseMessaging();
this.onTokenRefreshListener = tokenRefreshListener();
this.notificationListener = notificationListener();
/* Load Noti Setting is Enabled | Disabled */
getSingleData(storageKeys.notiEnabled).then((bool) => {
if (bool === null || bool === undefined) {
/* Store to MobX */
this.props.store.notiEnabled = 'true';
} else {
/* Store to MobX */
this.props.store.notiEnabled = bool;
}
});
}
/* ... */
componentWillUnmount() {
/* Spinner 숨기기 */
this.props.store.setLoginSpinnerVisible(false);
/* 메시지 창 숨기기 */
this.props.store.setLoginAlertVisible(false);
/* Firebase Noti */
this.onTokenRefreshListener();
this.notificationListener();
}
/* ... */
render() {
{ /* ... */ }
}
}
export default Page;
/**
Firebase Admin SDK implementation of Cloud Messaging in AWS Lambda
reference: https://firebase.google.com/docs/cloud-messaging/admin/send-messages?hl=ko
*/
const admin = require('firebase-admin');
const serviceAccount = require('./firebase/xxxx-firebase-adminsdk-xxxx.json');
exports.handler = (event, context, callback) => {
const done = (err, res) => callback(null, {
statusCode: err ? '400' : '200',
body: err ? err.message : JSON.stringify(res),
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
});
/**
* @name request_parameters
* @param devices registrationToken
* @param projInfo notification and data
*/
const postBody = (typeof event.body === 'string') ? JSON.parse(event.body) : event.body;
console.log('[sendNoti:postBody]', postBody);
if (!postBody) {
return done({message: 'warming'}, null);
}
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
}
if (postBody.devices.length === 0) {
return done({message: 'nothing to send to device'}, null);
}
const registrationToken = postBody.devices;
const projInfo = postBody.projInfo;
const payload = {
notification: {
title: /* title */,
body: projInfo.msg, // PROJHUB_MGMT_PUSH_LOG.CONTEXT
sound: 'default',
},
data: {
/* datas you want to send */
}
};
const options = {
priority: 'high',
timeToLive: 60 * 60
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().sendToDevice(registrationToken, payload, options).then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
console.log('results:', response.results);
// Lambda같은 serverless function에선 MySQL의 인스턴스 닫듯이 firebase app객체도 즉각 없애야 됨
admin.app('[DEFAULT]').delete();
return done(null, {result: 1});
})
.catch((error) => {
console.log('Error sending message:', error);
// Lambda같은 serverless function에선 MySQL의 인스턴스 닫듯이 firebase app객체도 즉각 없애야 됨
admin.app('[DEFAULT]').delete();
return done(error, null);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment