2019-10-17 16:00:50 +00:00
|
|
|
const moment = require('moment');
|
|
|
|
|
const { Expo } = require('expo-server-sdk');
|
2019-10-03 19:37:56 +00:00
|
|
|
const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper');
|
|
|
|
|
const models = require('../../core/models');
|
2019-10-17 16:00:50 +00:00
|
|
|
|
|
|
|
|
// Create a new Expo SDK client
|
|
|
|
|
const expo = new Expo();
|
2019-10-03 19:37:56 +00:00
|
|
|
|
2019-10-14 15:25:35 +00:00
|
|
|
const extraMethods = {
|
|
|
|
|
|
|
|
|
|
isValidPushToken: (token) => {
|
|
|
|
|
return Expo.isExpoPushToken(token);
|
|
|
|
|
},
|
|
|
|
|
|
2019-10-17 16:00:50 +00:00
|
|
|
sendNotification: async (messages) => {
|
2019-10-14 15:25:35 +00:00
|
|
|
|
|
|
|
|
// The Expo push notification service accepts batches of notifications so
|
|
|
|
|
// that you don't need to send 1000 requests to send 1000 notifications. We
|
|
|
|
|
// recommend you batch your notifications to reduce the number of requests
|
|
|
|
|
// and to compress them (notifications with similar content will get
|
|
|
|
|
// compressed).
|
2019-10-17 16:00:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* There is a limit on the number of push notifications (100) you can send at once.Use
|
|
|
|
|
* `chunkPushNotifications` to divide an array of push notification messages into appropriately
|
|
|
|
|
* sized chunks
|
|
|
|
|
*/
|
2019-10-14 15:25:35 +00:00
|
|
|
|
|
|
|
|
// Later, after the Expo push notification service has delivered the
|
|
|
|
|
// notifications to Apple or Google (usually quickly, but allow the the service
|
|
|
|
|
// up to 30 minutes when under load), a "receipt" for each notification is
|
|
|
|
|
// created. The receipts will be available for at least a day; stale receipts
|
|
|
|
|
// are deleted.
|
|
|
|
|
//
|
|
|
|
|
// The ID of each receipt is sent back in the response "ticket" for each
|
|
|
|
|
// notification. In summary, sending a notification produces a ticket, which
|
|
|
|
|
// contains a receipt ID you later use to get the receipt.
|
|
|
|
|
//
|
|
|
|
|
// The receipts may contain error codes to which you must respond. In
|
|
|
|
|
// particular, Apple or Google may block apps that continue to send
|
|
|
|
|
// notifications to devices that have blocked notifications or have uninstalled
|
|
|
|
|
// your app. Expo does not control this policy and sends back the feedback from
|
|
|
|
|
// Apple and Google so you can handle it appropriately.
|
2019-10-17 16:00:50 +00:00
|
|
|
|
|
|
|
|
let chunks = expo.chunkPushNotifications(messages);
|
|
|
|
|
let tickets = await _sendPushNotificationsAsync(chunks);
|
|
|
|
|
|
|
|
|
|
console.log(tickets);
|
2019-10-14 15:25:35 +00:00
|
|
|
let receiptIds = [];
|
2019-10-17 16:00:50 +00:00
|
|
|
let invalidTokens = [];
|
|
|
|
|
for (let [key, ticket] of tickets.entries()) {
|
2019-10-14 15:25:35 +00:00
|
|
|
// NOTE: Not all tickets have IDs; for example, tickets for notifications
|
|
|
|
|
// that could not be enqueued will have error information and no receipt ID.
|
|
|
|
|
if (ticket.id) {
|
2019-10-17 16:00:50 +00:00
|
|
|
receiptIds.push(ticket);
|
|
|
|
|
} else {
|
|
|
|
|
if ((ticket.status === 'error') && (ticket.details.error === 'DeviceNotRegistered')) {
|
|
|
|
|
invalidTokens.push({
|
|
|
|
|
...messages[key],
|
|
|
|
|
valid: false,
|
|
|
|
|
invalidated: moment(),
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-10-14 15:25:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 16:00:50 +00:00
|
|
|
console.log(receiptIds);
|
|
|
|
|
console.log(invalidTokens);
|
|
|
|
|
|
|
|
|
|
|
2019-10-14 15:25:35 +00:00
|
|
|
let receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds);
|
2019-10-17 16:00:50 +00:00
|
|
|
let xxx = await _getPushNotificationsResultAsync(receiptIdChunks);
|
|
|
|
|
|
|
|
|
|
let notifications = await _saveNotifications(messages, tickets);
|
|
|
|
|
|
|
|
|
|
return new Promise(function (resolve) { resolve(notifications) });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-17 16:59:18 +00:00
|
|
|
module.exports = generateService(models.Notification, extraMethods);
|
2019-10-17 16:00:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
const _sendPushNotificationsAsync = async function (chunks) {
|
|
|
|
|
let tickets = [];
|
|
|
|
|
// Send the chunks to the Expo push notification service. There are
|
|
|
|
|
// different strategies you could use. A simple one is to send one chunk at a
|
|
|
|
|
// time, which nicely spreads the load out over time:
|
|
|
|
|
for (let chunk of chunks) {
|
|
|
|
|
try {
|
|
|
|
|
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
|
|
|
|
|
tickets.push(...ticketChunk);
|
|
|
|
|
|
|
|
|
|
// NOTE: If a ticket contains an error code in ticket.details.error, you
|
|
|
|
|
// must handle it appropriately. The error codes are listed in the Expo
|
|
|
|
|
// documentation:
|
|
|
|
|
// https://docs.expo.io/versions/latest/guides/push-notifications#response-format
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Promise(function (resolve) { resolve(tickets) });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const _getPushNotificationsResultAsync = async function (receiptIdChunks) {
|
|
|
|
|
// Like sending notifications, there are different strategies you could use
|
|
|
|
|
// to retrieve batches of receipts from the Expo service.
|
|
|
|
|
|
|
|
|
|
let result = [];
|
|
|
|
|
|
|
|
|
|
console.log(receiptIdChunks);
|
|
|
|
|
|
|
|
|
|
for (let chunk of receiptIdChunks) {
|
|
|
|
|
try {
|
|
|
|
|
let receipts = await expo.getPushNotificationReceiptsAsync(chunk);
|
|
|
|
|
console.log('hola', receipts);
|
|
|
|
|
|
|
|
|
|
// The receipts specify whether Apple or Google successfully received the
|
|
|
|
|
// notification and information about an error, if one occurred.
|
|
|
|
|
for (let key in receipts) {
|
|
|
|
|
if (receipts[key].status === 'ok') {
|
|
|
|
|
result.push[receipts[key]];
|
|
|
|
|
continue;
|
|
|
|
|
} else if (receipts[key].status === 'error') {
|
|
|
|
|
console.error(`There was an error sending a notification: ${receipts[key].message}`);
|
|
|
|
|
if (receipts[key].details && receipts[key].details.error) {
|
|
|
|
|
// The error codes are listed in the Expo documentation:
|
|
|
|
|
// https://docs.expo.io/versions/latest/guides/push-notifications#response-format
|
|
|
|
|
// You must handle the errors appropriately.
|
|
|
|
|
console.error(`The error code is ${receipts[key].details.error}`);
|
2019-10-14 15:25:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-10-17 16:00:50 +00:00
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
}
|
2019-10-14 15:25:35 +00:00
|
|
|
}
|
2019-10-03 19:37:56 +00:00
|
|
|
|
2019-10-17 16:00:50 +00:00
|
|
|
return new Promise(function (resolve) { resolve(result) });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const _saveNotifications = async function (messages, tickets) {
|
|
|
|
|
let notifications = [];
|
|
|
|
|
messages.forEach(function (message, index) {
|
2019-10-17 16:59:18 +00:00
|
|
|
let notification = models.NotificationDetail.build({
|
2019-10-17 16:00:50 +00:00
|
|
|
...message,
|
|
|
|
|
ticket: tickets[index].id,
|
|
|
|
|
status: tickets[index].status,
|
|
|
|
|
error: (tickets[index].status === 'error') ? tickets[index].details.error : undefined,
|
|
|
|
|
});
|
|
|
|
|
notifications.push(notification);
|
|
|
|
|
});
|
2019-10-03 19:37:56 +00:00
|
|
|
|
2019-10-17 16:00:50 +00:00
|
|
|
return new Promise(function (resolve) { resolve(notifications) });
|
|
|
|
|
}
|