1+ import {
2+ Expo ,
3+ ExpoPushErrorReceipt ,
4+ ExpoPushMessage ,
5+ ExpoPushReceipt ,
6+ ExpoPushTicket ,
7+ } from 'expo-server-sdk'
18import mongoose from 'mongoose'
29import { Notification } from '../../models/notification.model'
3- import { pubSubPublish } from '../../resolvers/notification.resolvers'
4- import { User } from '../../models/user'
510import { Profile } from '../../models/profile.model'
11+ import { User } from '../../models/user'
12+ import { pubSubPublish } from '../../resolvers/notification.resolvers'
13+
14+ export type NotificationData = {
15+ redirectURI ?: string
16+ criteria : {
17+ type : 'PUBLIC' | 'PERSONAL' | 'TEAM' | 'ORGANIZATION'
18+ value : string
19+ }
20+ }
21+
22+ let expo = new Expo ( {
23+ accessToken : process . env . EXPO_ACCESS_TOKEN ,
24+ } )
625
726export const pushNotification = async (
827 receiver : mongoose . Types . ObjectId ,
@@ -29,8 +48,113 @@ export const pushNotification = async (
2948 id : notification . id ,
3049 sender : { profile : profile ?. toObject ( ) } ,
3150 }
32- const userExists = await User . findOne ( { _id : receiver } )
33- if ( userExists && userExists . pushNotifications ) {
51+ const receivingUser = await User . findOne ( { _id : receiver } )
52+ if ( receivingUser && receivingUser . pushNotifications ) {
3453 pubSubPublish ( sanitizedNotification )
54+
55+ if ( receivingUser . pushNotificationTokens . length > 0 ) {
56+ const notificationData : NotificationData = {
57+ redirectURI : `/dashboard/trainee${
58+ notification . type ? '/' + notification . type : ''
59+ } `,
60+ criteria : { type : 'PERSONAL' , value : receivingUser . id } ,
61+ }
62+
63+ console . log ( 'Sending push notifications' )
64+
65+ sendPushNotifications (
66+ receivingUser . pushNotificationTokens ,
67+ `${ capitalizeString ( notification . type ) } notification` ,
68+ notification . message ,
69+ notificationData
70+ )
71+ }
72+ }
73+ }
74+
75+ const capitalizeString = ( str ?: string ) => {
76+ str = str || 'New'
77+ return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 )
78+ }
79+
80+ const sendPushNotifications = async (
81+ pushTokens : string [ ] ,
82+ title : string ,
83+ body : string ,
84+ data : NotificationData
85+ ) => {
86+ // Create the messages that you want to send to clients
87+ let messages : ExpoPushMessage [ ] = [ ]
88+ for ( let pushToken of pushTokens ) {
89+ // Each push token looks like ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]
90+ // Check that all your push tokens appear to be valid Expo push tokens
91+ if ( ! Expo . isExpoPushToken ( pushToken ) ) {
92+ console . error ( `Push token ${ pushToken } is not a valid Expo push token` )
93+ continue
94+ }
95+
96+ messages . push ( {
97+ to : pushToken ,
98+ title : title ,
99+ body : body ,
100+ data : data ,
101+ } )
35102 }
103+
104+ // The Expo push notification service accepts batches of notifications so
105+ // that you don't need to send 1000 requests to send 1000 notifications.
106+ let chunks = expo . chunkPushNotifications ( messages )
107+ let tickets : ExpoPushTicket [ ] = [ ]
108+ ; ( async ( ) => {
109+ for ( let chunk of chunks ) {
110+ try {
111+ let ticketChunk = await expo . sendPushNotificationsAsync ( chunk )
112+ console . log ( ticketChunk )
113+ tickets . push ( ...ticketChunk )
114+ } catch ( error ) {
115+ console . error ( error )
116+ }
117+ }
118+ } ) ( )
119+
120+ let receiptIds = [ ]
121+ for ( let ticket of tickets ) {
122+ // NOTE: Not all tickets have IDs; for example, tickets for notifications
123+ // that could not be enqueued will have error information and no receipt ID.
124+ if ( ticket . status === 'ok' ) {
125+ receiptIds . push ( ticket . id )
126+ }
127+
128+ if ( ticket . status === 'error' && ticket . details ) {
129+ console . error ( ticket . details . error )
130+ }
131+ }
132+
133+ let receiptIdChunks = expo . chunkPushNotificationReceiptIds ( receiptIds )
134+ ; ( async ( ) => {
135+ // Like sending notifications, there are different strategies you could use
136+ // to retrieve batches of receipts from the Expo service.
137+ for ( let chunk of receiptIdChunks ) {
138+ try {
139+ let receipts = await expo . getPushNotificationReceiptsAsync ( chunk )
140+ console . log ( receipts )
141+
142+ // The receipts specify whether Apple or Google successfully received the
143+ // notification and information about an error, if one occurred.
144+ for ( let receiptId in receipts ) {
145+ let { status, details } : ExpoPushReceipt | ExpoPushErrorReceipt =
146+ receipts [ receiptId ]
147+ if ( status === 'ok' ) {
148+ continue
149+ } else if ( status === 'error' ) {
150+ console . error (
151+ `There was an error sending a notification: ${ details } `
152+ )
153+ }
154+ }
155+ } catch ( error ) {
156+ console . error ( error )
157+ }
158+ }
159+ } ) ( )
36160}
0 commit comments