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 +13 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java +2 -0
- package/android/src/main/java/expo/modules/notifications/notifications/model/NotificationContent.java +10 -0
- package/android/src/main/java/expo/modules/notifications/notifications/presentation/builders/ExpoNotificationBuilder.kt +39 -25
- package/build/getExpoPushTokenAsync.d.ts.map +1 -1
- package/build/getExpoPushTokenAsync.js +4 -1
- package/build/getExpoPushTokenAsync.js.map +1 -1
- package/package.json +2 -2
- package/src/getExpoPushTokenAsync.ts +5 -1
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
|
package/android/build.gradle
CHANGED
|
@@ -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.
|
|
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.
|
|
18
|
+
versionName '0.29.11'
|
|
19
19
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
20
20
|
}
|
|
21
21
|
|
package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
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
|
-
|
|
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(
|