expo-notifications 0.29.9 → 0.29.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,19 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.29.11 — 2024-12-05
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [android] fix data serialization for notifications with `ChannelAwareTrigger` ([#33354](https://github.com/expo/expo/pull/33354) by [@alextoudic](https://github.com/alextoudic))
18
+ - Add additional fallback value for project ID in `getExpoPushTokenAsync` ([#33359](https://github.com/expo/expo/pull/33359) by [@bradjones1](https://github.com/bradjones1))
19
+
20
+ ## 0.29.10 — 2024-12-02
21
+
22
+ ### 🐛 Bug fixes
23
+
24
+ - [android] fix notifications with custom sounds treated as silent ([#33311](https://github.com/expo/expo/pull/33311) by [@pennersr](https://github.com/pennersr))
25
+
13
26
  ## 0.29.9 — 2024-11-29
14
27
 
15
28
  ### 🐛 Bug fixes
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
2
2
  apply plugin: 'kotlin-parcelize'
3
3
 
4
4
  group = 'host.exp.exponent'
5
- version = '0.29.9'
5
+ version = '0.29.11'
6
6
 
7
7
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
8
8
  apply from: expoModulesCorePlugin
@@ -15,7 +15,7 @@ android {
15
15
  namespace "expo.modules.notifications"
16
16
  defaultConfig {
17
17
  versionCode 21
18
- versionName '0.29.9'
18
+ versionName '0.29.11'
19
19
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20
20
  }
21
21
 
@@ -30,6 +30,7 @@ import expo.modules.notifications.notifications.model.NotificationResponse;
30
30
  import expo.modules.notifications.notifications.model.TextInputNotificationResponse;
31
31
  import expo.modules.notifications.notifications.model.triggers.FirebaseNotificationTrigger;
32
32
 
33
+ import expo.modules.notifications.notifications.triggers.ChannelAwareTrigger;
33
34
  import expo.modules.notifications.notifications.triggers.DailyTrigger;
34
35
  import expo.modules.notifications.notifications.triggers.DateTrigger;
35
36
  import expo.modules.notifications.notifications.triggers.MonthlyTrigger;
@@ -79,6 +80,7 @@ public class NotificationSerializer {
79
80
  }
80
81
  } else if(
81
82
  requestTrigger instanceof SchedulableNotificationTrigger ||
83
+ requestTrigger instanceof ChannelAwareTrigger ||
82
84
  requestTrigger == null
83
85
  ) {
84
86
  JSONObject body = request.getContent().getBody();
@@ -22,6 +22,7 @@ import java.io.Serializable;
22
22
  import androidx.annotation.NonNull;
23
23
  import androidx.annotation.Nullable;
24
24
 
25
+ import androidx.annotation.VisibleForTesting;
25
26
  import expo.modules.notifications.notifications.enums.NotificationPriority;
26
27
  import expo.modules.notifications.notifications.interfaces.INotificationContent;
27
28
  import kotlin.coroutines.Continuation;
@@ -320,6 +321,15 @@ public class NotificationContent implements Parcelable, Serializable, INotificat
320
321
  return this;
321
322
  }
322
323
 
324
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
325
+ Builder disableVibrations() {
326
+ // remote notifications can come without vibrations
327
+ // we use this method do emulate that in tests
328
+ content.mShouldUseDefaultVibrationPattern = false;
329
+ content.mVibrationPattern = null;
330
+ return this;
331
+ }
332
+
323
333
  public Builder setVibrationPattern(long[] vibrationPattern) {
324
334
  content.mShouldUseDefaultVibrationPattern = false;
325
335
  content.mVibrationPattern = vibrationPattern;
@@ -5,6 +5,7 @@ import android.content.Context
5
5
  import android.content.pm.PackageManager
6
6
  import android.graphics.Bitmap
7
7
  import android.graphics.BitmapFactory
8
+ import android.os.Build
8
9
  import android.os.Bundle
9
10
  import android.os.Parcel
10
11
  import android.provider.Settings
@@ -13,6 +14,7 @@ import androidx.core.app.NotificationCompat
13
14
  import androidx.core.app.RemoteInput
14
15
  import expo.modules.notifications.notifications.SoundResolver
15
16
  import expo.modules.notifications.notifications.enums.NotificationPriority
17
+ import expo.modules.notifications.notifications.interfaces.INotificationContent
16
18
  import expo.modules.notifications.notifications.model.NotificationAction
17
19
  import expo.modules.notifications.notifications.model.NotificationCategory
18
20
  import expo.modules.notifications.notifications.model.NotificationRequest
@@ -110,31 +112,7 @@ open class ExpoNotificationBuilder(
110
112
  notificationContent.badgeCount?.toInt()?.let { builder.setNumber(it) }
111
113
  notificationContent.categoryId?.let { addActionsToBuilder(builder, it) }
112
114
 
113
- val shouldPlayDefaultSound = shouldPlaySound() && content.shouldPlayDefaultSound
114
- if (shouldPlayDefaultSound && shouldVibrate()) {
115
- builder.setDefaults(NotificationCompat.DEFAULT_ALL) // set sound, vibration and lights
116
- } else if (shouldVibrate()) {
117
- builder.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
118
- } else if (shouldPlayDefaultSound) {
119
- builder.setDefaults(NotificationCompat.DEFAULT_SOUND)
120
- } else {
121
- // Notification will not vibrate or play sound, regardless of channel
122
- builder.setSilent(true)
123
- }
124
-
125
- if (shouldPlaySound() && content.soundName != null) {
126
- content.soundName?.let { soundName ->
127
- val soundUri = SoundResolver(context).resolve(soundName)
128
- builder.setSound(soundUri)
129
- }
130
- } else if (shouldPlayDefaultSound) {
131
- builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
132
- }
133
-
134
- val vibrationPatternOverride = content.vibrationPattern
135
- if (shouldVibrate() && vibrationPatternOverride != null) {
136
- builder.setVibrate(vibrationPatternOverride)
137
- }
115
+ applySoundsAndVibrations(content, builder)
138
116
 
139
117
  if (content.body != null) {
140
118
  // Add body - JSON data - to extras
@@ -178,6 +156,42 @@ open class ExpoNotificationBuilder(
178
156
  return builder.build()
179
157
  }
180
158
 
159
+ private fun applySoundsAndVibrations(content: INotificationContent, builder: NotificationCompat.Builder) {
160
+ val shouldPlaySound = shouldPlaySound()
161
+ val shouldVibrate = shouldVibrate()
162
+
163
+ if (!shouldPlaySound && !shouldVibrate) {
164
+ // Notification will not vibrate or play sound, regardless of channel
165
+ builder.setSilent(true)
166
+ }
167
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
168
+ // the calls below are ignored on Android O and newer because the sound and vibration are set in the channel
169
+ val shouldPlayDefaultSound = shouldPlaySound && content.shouldPlayDefaultSound
170
+ val shouldUseDefaultVibrationPattern = shouldVibrate && content.shouldUseDefaultVibrationPattern
171
+ if (shouldUseDefaultVibrationPattern && shouldPlayDefaultSound) {
172
+ builder.setDefaults(NotificationCompat.DEFAULT_ALL)
173
+ } else {
174
+ if (shouldPlaySound) {
175
+ if (content.soundName != null) {
176
+ val soundUri = SoundResolver(context).resolve(content.soundName)
177
+ builder.setSound(soundUri)
178
+ } else if (shouldPlayDefaultSound) {
179
+ builder.setDefaults(NotificationCompat.DEFAULT_SOUND)
180
+ builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
181
+ }
182
+ }
183
+ if (shouldVibrate) {
184
+ val vibrationPatternOverride = content.vibrationPattern
185
+ if (vibrationPatternOverride != null) {
186
+ builder.setVibrate(vibrationPatternOverride)
187
+ } else if (shouldUseDefaultVibrationPattern) {
188
+ builder.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+
181
195
  /**
182
196
  * Marshalls [NotificationRequest] into to a byte array.
183
197
  *
@@ -1 +1 @@
1
- {"version":3,"file":"getExpoPushTokenAsync.d.ts","sourceRoot":"","sources":["../src/getExpoPushTokenAsync.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAKtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAA8B,qBAAqB,CACjD,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,aAAa,CAAC,CAmFxB"}
1
+ {"version":3,"file":"getExpoPushTokenAsync.d.ts","sourceRoot":"","sources":["../src/getExpoPushTokenAsync.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAKtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAA8B,qBAAqB,CACjD,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,aAAa,CAAC,CAuFxB"}
@@ -45,7 +45,10 @@ const productionBaseUrl = 'https://exp.host/--/api/v2/';
45
45
  export default async function getExpoPushTokenAsync(options = {}) {
46
46
  const devicePushToken = options.devicePushToken || (await getDevicePushTokenAsync());
47
47
  const deviceId = options.deviceId || (await getDeviceIdAsync());
48
- const projectId = options.projectId || Constants.easConfig?.projectId;
48
+ // Depending on the runtime environment, the default may be located in various places.
49
+ const projectId = options.projectId ||
50
+ Constants.easConfig?.projectId ||
51
+ Constants.expoConfig?.extra?.eas?.projectId;
49
52
  if (!projectId) {
50
53
  throw new CodedError('ERR_NOTIFICATIONS_NO_EXPERIENCE_ID', `No "projectId" found. If "projectId" can't be inferred from the manifest (for instance, in bare workflow), you have to pass it in yourself.`);
51
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"getExpoPushTokenAsync.js","sourceRoot":"","sources":["../src/getExpoPushTokenAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Application from 'expo-application';\nimport Constants from 'expo-constants';\nimport { Platform, CodedError, UnavailabilityError } from 'expo-modules-core';\n\nimport { setAutoServerRegistrationEnabledAsync } from './DevicePushTokenAutoRegistration.fx';\nimport ServerRegistrationModule from './ServerRegistrationModule';\nimport { DevicePushToken, ExpoPushToken, ExpoPushTokenOptions } from './Tokens.types';\nimport getDevicePushTokenAsync from './getDevicePushTokenAsync';\n\nconst productionBaseUrl = 'https://exp.host/--/api/v2/';\n\n/**\n * Returns an Expo token that can be used to send a push notification to the device using Expo's push notifications service.\n *\n * This method makes requests to the Expo's servers. It can get rejected in cases where the request itself fails\n * (for example, due to the device being offline, experiencing a network timeout, or other HTTPS request failures).\n * To provide offline support to your users, you should `try/catch` this method and implement retry logic to attempt\n * to get the push token later, once the device is back online.\n *\n * > For Expo's backend to be able to send notifications to your app, you will need to provide it with push notification keys.\n * For more information, see [credentials](/push-notifications/push-notifications-setup/#get-credentials-for-development-builds) in the push notifications setup.\n *\n * @param options Object allowing you to pass in push notification configuration.\n * @return Returns a `Promise` that resolves to an object representing acquired push token.\n * @header fetch\n *\n * @example\n * ```ts\n * import * as Notifications from 'expo-notifications';\n *\n * export async function registerForPushNotificationsAsync(userId: string) {\n * const expoPushToken = await Notifications.getExpoPushTokenAsync({\n * projectId: 'your-project-id',\n * });\n *\n * await fetch('https://example.com/', {\n * method: 'POST',\n * headers: {\n * 'Content-Type': 'application/json',\n * },\n * body: JSON.stringify({\n * userId,\n * expoPushToken,\n * }),\n * });\n * }\n * ```\n */\nexport default async function getExpoPushTokenAsync(\n options: ExpoPushTokenOptions = {}\n): Promise<ExpoPushToken> {\n const devicePushToken = options.devicePushToken || (await getDevicePushTokenAsync());\n\n const deviceId = options.deviceId || (await getDeviceIdAsync());\n const projectId = options.projectId || Constants.easConfig?.projectId;\n\n if (!projectId) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_NO_EXPERIENCE_ID',\n `No \"projectId\" found. If \"projectId\" can't be inferred from the manifest (for instance, in bare workflow), you have to pass it in yourself.`\n );\n }\n\n const applicationId = options.applicationId || Application.applicationId;\n if (!applicationId) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_NO_APPLICATION_ID',\n `No \"applicationId\" found. If it can't be inferred from native configuration by expo-application, you have to pass it in yourself.`\n );\n }\n const type = options.type || getTypeOfToken(devicePushToken);\n const development = options.development || (await shouldUseDevelopmentNotificationService());\n\n const baseUrl = options.baseUrl ?? productionBaseUrl;\n const url = options.url ?? `${baseUrl}push/getExpoPushToken`;\n\n const body = {\n type,\n deviceId: deviceId.toLowerCase(),\n development,\n appId: applicationId,\n deviceToken: getDeviceToken(devicePushToken),\n projectId,\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n }).catch((error) => {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_NETWORK_ERROR',\n `Error encountered while fetching Expo token: ${error}.`\n );\n });\n\n if (!response.ok) {\n const statusInfo = response.statusText || response.status;\n let body: string | undefined = undefined;\n try {\n body = await response.text();\n } catch {\n // do nothing\n }\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Error encountered while fetching Expo token, expected an OK response, received: ${statusInfo} (body: \"${body}\").`\n );\n }\n\n const expoPushToken = getExpoPushToken(await parseResponse(response));\n\n try {\n if (options.url || options.baseUrl) {\n console.debug(\n `[expo-notifications] Since the URL endpoint to register in has been customized in the options, expo-notifications won't try to auto-update the device push token on the server.`\n );\n } else {\n await setAutoServerRegistrationEnabledAsync(true);\n }\n } catch (e) {\n console.warn(\n '[expo-notifications] Could not enable automatically registering new device tokens with the Expo notification service',\n e\n );\n }\n\n return {\n type: 'expo',\n data: expoPushToken,\n };\n}\n\nasync function parseResponse(response: Response) {\n try {\n return await response.json();\n } catch {\n try {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Expected a JSON response from server when fetching Expo token, received body: ${JSON.stringify(\n await response.text()\n )}.`\n );\n } catch {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Expected a JSON response from server when fetching Expo token, received response: ${JSON.stringify(\n response\n )}.`\n );\n }\n }\n}\n\nfunction getExpoPushToken(data: any) {\n if (\n !data ||\n !(typeof data === 'object') ||\n !data.data ||\n !(typeof data.data === 'object') ||\n !data.data.expoPushToken ||\n !(typeof data.data.expoPushToken === 'string')\n ) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Malformed response from server, expected \"{ data: { expoPushToken: string } }\", received: ${JSON.stringify(\n data,\n null,\n 2\n )}.`\n );\n }\n\n return data.data.expoPushToken as string;\n}\n\n// Same as in DevicePushTokenAutoRegistration\nasync function getDeviceIdAsync() {\n try {\n if (!ServerRegistrationModule.getInstallationIdAsync) {\n throw new UnavailabilityError('ExpoServerRegistrationModule', 'getInstallationIdAsync');\n }\n\n return await ServerRegistrationModule.getInstallationIdAsync();\n } catch (e) {\n throw new CodedError(\n 'ERR_NOTIF_DEVICE_ID',\n `Could not have fetched installation ID of the application: ${e}.`\n );\n }\n}\n\nfunction getDeviceToken(devicePushToken: DevicePushToken) {\n if (typeof devicePushToken.data === 'string') {\n return devicePushToken.data;\n }\n\n return JSON.stringify(devicePushToken.data);\n}\n\n// Same as in DevicePushTokenAutoRegistration\nasync function shouldUseDevelopmentNotificationService() {\n if (Platform.OS === 'ios') {\n try {\n const notificationServiceEnvironment =\n await Application.getIosPushNotificationServiceEnvironmentAsync();\n if (notificationServiceEnvironment === 'development') {\n return true;\n }\n } catch {\n // We can't do anything here, we'll fallback to false then.\n }\n }\n\n return false;\n}\n\n// Same as in DevicePushTokenAutoRegistration\nfunction getTypeOfToken(devicePushToken: DevicePushToken) {\n switch (devicePushToken.type) {\n case 'ios':\n return 'apns';\n case 'android':\n return 'fcm';\n // This probably will error on server, but let's make this function future-safe.\n default:\n return devicePushToken.type;\n }\n}\n"]}
1
+ {"version":3,"file":"getExpoPushTokenAsync.js","sourceRoot":"","sources":["../src/getExpoPushTokenAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Application from 'expo-application';\nimport Constants from 'expo-constants';\nimport { Platform, CodedError, UnavailabilityError } from 'expo-modules-core';\n\nimport { setAutoServerRegistrationEnabledAsync } from './DevicePushTokenAutoRegistration.fx';\nimport ServerRegistrationModule from './ServerRegistrationModule';\nimport { DevicePushToken, ExpoPushToken, ExpoPushTokenOptions } from './Tokens.types';\nimport getDevicePushTokenAsync from './getDevicePushTokenAsync';\n\nconst productionBaseUrl = 'https://exp.host/--/api/v2/';\n\n/**\n * Returns an Expo token that can be used to send a push notification to the device using Expo's push notifications service.\n *\n * This method makes requests to the Expo's servers. It can get rejected in cases where the request itself fails\n * (for example, due to the device being offline, experiencing a network timeout, or other HTTPS request failures).\n * To provide offline support to your users, you should `try/catch` this method and implement retry logic to attempt\n * to get the push token later, once the device is back online.\n *\n * > For Expo's backend to be able to send notifications to your app, you will need to provide it with push notification keys.\n * For more information, see [credentials](/push-notifications/push-notifications-setup/#get-credentials-for-development-builds) in the push notifications setup.\n *\n * @param options Object allowing you to pass in push notification configuration.\n * @return Returns a `Promise` that resolves to an object representing acquired push token.\n * @header fetch\n *\n * @example\n * ```ts\n * import * as Notifications from 'expo-notifications';\n *\n * export async function registerForPushNotificationsAsync(userId: string) {\n * const expoPushToken = await Notifications.getExpoPushTokenAsync({\n * projectId: 'your-project-id',\n * });\n *\n * await fetch('https://example.com/', {\n * method: 'POST',\n * headers: {\n * 'Content-Type': 'application/json',\n * },\n * body: JSON.stringify({\n * userId,\n * expoPushToken,\n * }),\n * });\n * }\n * ```\n */\nexport default async function getExpoPushTokenAsync(\n options: ExpoPushTokenOptions = {}\n): Promise<ExpoPushToken> {\n const devicePushToken = options.devicePushToken || (await getDevicePushTokenAsync());\n\n const deviceId = options.deviceId || (await getDeviceIdAsync());\n // Depending on the runtime environment, the default may be located in various places.\n const projectId =\n options.projectId ||\n Constants.easConfig?.projectId ||\n Constants.expoConfig?.extra?.eas?.projectId;\n\n if (!projectId) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_NO_EXPERIENCE_ID',\n `No \"projectId\" found. If \"projectId\" can't be inferred from the manifest (for instance, in bare workflow), you have to pass it in yourself.`\n );\n }\n\n const applicationId = options.applicationId || Application.applicationId;\n if (!applicationId) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_NO_APPLICATION_ID',\n `No \"applicationId\" found. If it can't be inferred from native configuration by expo-application, you have to pass it in yourself.`\n );\n }\n const type = options.type || getTypeOfToken(devicePushToken);\n const development = options.development || (await shouldUseDevelopmentNotificationService());\n\n const baseUrl = options.baseUrl ?? productionBaseUrl;\n const url = options.url ?? `${baseUrl}push/getExpoPushToken`;\n\n const body = {\n type,\n deviceId: deviceId.toLowerCase(),\n development,\n appId: applicationId,\n deviceToken: getDeviceToken(devicePushToken),\n projectId,\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n }).catch((error) => {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_NETWORK_ERROR',\n `Error encountered while fetching Expo token: ${error}.`\n );\n });\n\n if (!response.ok) {\n const statusInfo = response.statusText || response.status;\n let body: string | undefined = undefined;\n try {\n body = await response.text();\n } catch {\n // do nothing\n }\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Error encountered while fetching Expo token, expected an OK response, received: ${statusInfo} (body: \"${body}\").`\n );\n }\n\n const expoPushToken = getExpoPushToken(await parseResponse(response));\n\n try {\n if (options.url || options.baseUrl) {\n console.debug(\n `[expo-notifications] Since the URL endpoint to register in has been customized in the options, expo-notifications won't try to auto-update the device push token on the server.`\n );\n } else {\n await setAutoServerRegistrationEnabledAsync(true);\n }\n } catch (e) {\n console.warn(\n '[expo-notifications] Could not enable automatically registering new device tokens with the Expo notification service',\n e\n );\n }\n\n return {\n type: 'expo',\n data: expoPushToken,\n };\n}\n\nasync function parseResponse(response: Response) {\n try {\n return await response.json();\n } catch {\n try {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Expected a JSON response from server when fetching Expo token, received body: ${JSON.stringify(\n await response.text()\n )}.`\n );\n } catch {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Expected a JSON response from server when fetching Expo token, received response: ${JSON.stringify(\n response\n )}.`\n );\n }\n }\n}\n\nfunction getExpoPushToken(data: any) {\n if (\n !data ||\n !(typeof data === 'object') ||\n !data.data ||\n !(typeof data.data === 'object') ||\n !data.data.expoPushToken ||\n !(typeof data.data.expoPushToken === 'string')\n ) {\n throw new CodedError(\n 'ERR_NOTIFICATIONS_SERVER_ERROR',\n `Malformed response from server, expected \"{ data: { expoPushToken: string } }\", received: ${JSON.stringify(\n data,\n null,\n 2\n )}.`\n );\n }\n\n return data.data.expoPushToken as string;\n}\n\n// Same as in DevicePushTokenAutoRegistration\nasync function getDeviceIdAsync() {\n try {\n if (!ServerRegistrationModule.getInstallationIdAsync) {\n throw new UnavailabilityError('ExpoServerRegistrationModule', 'getInstallationIdAsync');\n }\n\n return await ServerRegistrationModule.getInstallationIdAsync();\n } catch (e) {\n throw new CodedError(\n 'ERR_NOTIF_DEVICE_ID',\n `Could not have fetched installation ID of the application: ${e}.`\n );\n }\n}\n\nfunction getDeviceToken(devicePushToken: DevicePushToken) {\n if (typeof devicePushToken.data === 'string') {\n return devicePushToken.data;\n }\n\n return JSON.stringify(devicePushToken.data);\n}\n\n// Same as in DevicePushTokenAutoRegistration\nasync function shouldUseDevelopmentNotificationService() {\n if (Platform.OS === 'ios') {\n try {\n const notificationServiceEnvironment =\n await Application.getIosPushNotificationServiceEnvironmentAsync();\n if (notificationServiceEnvironment === 'development') {\n return true;\n }\n } catch {\n // We can't do anything here, we'll fallback to false then.\n }\n }\n\n return false;\n}\n\n// Same as in DevicePushTokenAutoRegistration\nfunction getTypeOfToken(devicePushToken: DevicePushToken) {\n switch (devicePushToken.type) {\n case 'ios':\n return 'apns';\n case 'android':\n return 'fcm';\n // This probably will error on server, but let's make this function future-safe.\n default:\n return devicePushToken.type;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-notifications",
3
- "version": "0.29.9",
3
+ "version": "0.29.11",
4
4
  "description": "Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -55,5 +55,5 @@
55
55
  "react": "*",
56
56
  "react-native": "*"
57
57
  },
58
- "gitHead": "728158f99d680cf64fb06f08301d3806c18c6f63"
58
+ "gitHead": "a1fac063b47a647f2a9737e201d502066c52d4b0"
59
59
  }
@@ -52,7 +52,11 @@ export default async function getExpoPushTokenAsync(
52
52
  const devicePushToken = options.devicePushToken || (await getDevicePushTokenAsync());
53
53
 
54
54
  const deviceId = options.deviceId || (await getDeviceIdAsync());
55
- const projectId = options.projectId || Constants.easConfig?.projectId;
55
+ // Depending on the runtime environment, the default may be located in various places.
56
+ const projectId =
57
+ options.projectId ||
58
+ Constants.easConfig?.projectId ||
59
+ Constants.expoConfig?.extra?.eas?.projectId;
56
60
 
57
61
  if (!projectId) {
58
62
  throw new CodedError(