EXPO.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. "use strict";
  2. import Parse from 'parse';
  3. import log from 'npmlog';
  4. import { Expo } from 'expo-server-sdk';
  5. const LOG_PREFIX = 'parse-server-push-adapter EXPO';
  6. function expoResultToParseResponse(result) {
  7. if (result.status === 'ok') {
  8. return result;
  9. } else {
  10. // ParseServer looks for "error", and supports ceratin codes like 'NotRegistered' for
  11. // cleanup. Expo returns slighyly different ones so changing to match what is expected
  12. // This can be taken out if the responsibility gets moved to the adapter itself.
  13. const error = result.message === 'DeviceNotRegistered' ?
  14. 'NotRegistered' : result.message;
  15. return {
  16. error,
  17. ...result
  18. }
  19. }
  20. }
  21. export class EXPO {
  22. expo = undefined;
  23. /**
  24. * Create a new EXPO push adapter. Based on Web Adapter.
  25. *
  26. * @param {Object} args https://github.com/expo/expo-server-sdk-node / https://docs.expo.dev/push-notifications/sending-notifications/
  27. */
  28. constructor(args) {
  29. if (typeof args !== 'object') {
  30. throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'EXPO Push Configuration is invalid');
  31. }
  32. this.expo = new Expo(args)
  33. this.options = args;
  34. }
  35. /**
  36. * Send Expo push notification request.
  37. *
  38. * @param {Object} data The data we need to send, the format is the same with api request body
  39. * @param {Array} devices An array of devices
  40. * @returns {Object} A promise which is resolved immediately
  41. */
  42. async send(data, devices) {
  43. const coreData = data && data.data;
  44. if (!coreData || !devices || !Array.isArray(devices)) {
  45. log.warn(LOG_PREFIX, 'invalid push payload');
  46. return;
  47. }
  48. const devicesMap = devices.reduce((memo, device) => {
  49. memo[device.deviceToken] = device;
  50. return memo;
  51. }, {});
  52. const deviceTokens = Object.keys(devicesMap);
  53. const resolvers = [];
  54. const promises = deviceTokens.map(() => new Promise(resolve => resolvers.push(resolve)));
  55. const length = deviceTokens.length;
  56. log.verbose(LOG_PREFIX, `sending to ${length} ${length > 1 ? 'devices' : 'device'}`);
  57. const response = await this.sendNotifications(coreData, deviceTokens);
  58. log.verbose(LOG_PREFIX, `EXPO Response: %d sent`, response.length);
  59. deviceTokens.forEach((token, index) => {
  60. const resolve = resolvers[index];
  61. const result = response[index];
  62. const device = devicesMap[token];
  63. const resolution = {
  64. transmitted: result.status === 'ok',
  65. device: {
  66. ...device,
  67. pushType: 'expo'
  68. },
  69. response: expoResultToParseResponse(result),
  70. };
  71. resolve(resolution);
  72. });
  73. return Promise.all(promises);
  74. }
  75. /**
  76. * Send multiple Expo push notification request.
  77. *
  78. * @param {Object} payload The data we need to send, the format is the same with api request body
  79. * @param {Array} deviceTokens An array of devicesTokens
  80. * @param {Object} options The options for the request
  81. * @returns {Object} A promise which is resolved immediately
  82. */
  83. async sendNotifications({alert, body, ...payload}, deviceTokens) {
  84. const messages = deviceTokens.map((token) => ({
  85. to: token,
  86. body: body || alert,
  87. ...payload
  88. }));
  89. return await this.expo.sendPushNotificationsAsync(messages);
  90. }
  91. }
  92. export default EXPO;