@fishonfire/bubbles-expo 0.1.0 → 0.1.1
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/README.md +17 -2
- package/dist/{chunk-6NPXFGQX.mjs → chunk-3BAGWXHY.mjs} +85 -0
- package/dist/index.d.mts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +208 -1
- package/dist/index.mjs +125 -3
- package/dist/register-task.mjs +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -162,10 +162,25 @@ export function EnableNotificationsButton() {
|
|
|
162
162
|
}
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
`useBubblesNotifications()` also exposes `deviceId`, `pushToken`, `permissionStatus`, `notificationsEnabled`, `tokenType`, and `error`.
|
|
165
|
+
`useBubblesNotifications()` also exposes `addDeviceAttribute`, `deviceId`, `pushToken`, `permissionStatus`, `notificationsEnabled`, `tokenType`, and `error`.
|
|
166
166
|
|
|
167
167
|
If your app already requested notification permissions, call `registerDevice({ requestPermissions: false })`.
|
|
168
168
|
|
|
169
|
+
## Add custom device attributes
|
|
170
|
+
|
|
171
|
+
After the provider has a backend `deviceId`, apps can add their own device attributes:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
const { addDeviceAttribute, deviceId } = useBubblesNotifications();
|
|
175
|
+
|
|
176
|
+
if (deviceId) {
|
|
177
|
+
void addDeviceAttribute('plan', {
|
|
178
|
+
tier: 'pro',
|
|
179
|
+
seats: 4,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
169
184
|
## Example application
|
|
170
185
|
A example application can be found at: https://github.com/fishonfire/bubbles-notifications-expo-example
|
|
171
186
|
|
|
@@ -177,4 +192,4 @@ A example application can be found at: https://github.com/fishonfire/bubbles-not
|
|
|
177
192
|
## Copyright and Licence
|
|
178
193
|
Copyright (c) 2026, Fish on Fire.
|
|
179
194
|
|
|
180
|
-
Source code is licensed under the [`GPL License`](https://github.com/fishonfire/bubbles-notifications-expo/blob/develop/LICENSE).
|
|
195
|
+
Source code is licensed under the [`GPL License`](https://github.com/fishonfire/bubbles-notifications-expo/blob/develop/LICENSE).
|
|
@@ -458,6 +458,78 @@ function throwAppAuthenticationError(error) {
|
|
|
458
458
|
}
|
|
459
459
|
throw error;
|
|
460
460
|
}
|
|
461
|
+
function normalizeDeviceAttributes(value) {
|
|
462
|
+
return normalizeDeviceAttributeObject(value, "attributes", /* @__PURE__ */ new WeakSet());
|
|
463
|
+
}
|
|
464
|
+
function normalizeDeviceAttributeValue(value, sourceDescription, seenValues) {
|
|
465
|
+
if (value === null) {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
469
|
+
return value;
|
|
470
|
+
}
|
|
471
|
+
if (typeof value === "number") {
|
|
472
|
+
if (!Number.isFinite(value)) {
|
|
473
|
+
failWithBubblesError(`${sourceDescription} must be a finite number.`);
|
|
474
|
+
}
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
if (Array.isArray(value)) {
|
|
478
|
+
if (seenValues.has(value)) {
|
|
479
|
+
failWithBubblesError(`${sourceDescription} must not contain cycles.`);
|
|
480
|
+
}
|
|
481
|
+
seenValues.add(value);
|
|
482
|
+
const normalizedValue = value.map(
|
|
483
|
+
(entry, index) => normalizeDeviceAttributeValue(
|
|
484
|
+
entry,
|
|
485
|
+
`${sourceDescription}[${index}]`,
|
|
486
|
+
seenValues
|
|
487
|
+
)
|
|
488
|
+
);
|
|
489
|
+
seenValues.delete(value);
|
|
490
|
+
return normalizedValue;
|
|
491
|
+
}
|
|
492
|
+
if (isPlainObject(value)) {
|
|
493
|
+
return normalizeDeviceAttributeObject(
|
|
494
|
+
value,
|
|
495
|
+
sourceDescription,
|
|
496
|
+
seenValues
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
failWithBubblesError(
|
|
500
|
+
`${sourceDescription} must be a JSON-compatible value.`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
function normalizeDeviceAttributeObject(value, sourceDescription, seenValues) {
|
|
504
|
+
if (!isPlainObject(value)) {
|
|
505
|
+
failWithBubblesError('"attributes" must be an object.');
|
|
506
|
+
}
|
|
507
|
+
const valuePrototype = Object.getPrototypeOf(value);
|
|
508
|
+
if (valuePrototype !== Object.prototype && valuePrototype !== null) {
|
|
509
|
+
failWithBubblesError(
|
|
510
|
+
`${sourceDescription} must be a JSON-compatible object.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
514
|
+
failWithBubblesError(
|
|
515
|
+
`${sourceDescription} must not contain symbol keys.`
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
if (seenValues.has(value)) {
|
|
519
|
+
failWithBubblesError(`${sourceDescription} must not contain cycles.`);
|
|
520
|
+
}
|
|
521
|
+
seenValues.add(value);
|
|
522
|
+
const normalizedValue = {};
|
|
523
|
+
for (const [attributeName, attributeValue] of Object.entries(value)) {
|
|
524
|
+
normalizedValue[attributeName] = normalizeDeviceAttributeValue(
|
|
525
|
+
attributeValue,
|
|
526
|
+
`${sourceDescription}.${attributeName}`,
|
|
527
|
+
seenValues
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
seenValues.delete(value);
|
|
531
|
+
return normalizedValue;
|
|
532
|
+
}
|
|
461
533
|
function extractDeviceIdFromDeviceResponse(value) {
|
|
462
534
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
463
535
|
return null;
|
|
@@ -497,6 +569,18 @@ async function updateBubblesDevice(options) {
|
|
|
497
569
|
throwAppAuthenticationError(error);
|
|
498
570
|
}
|
|
499
571
|
}
|
|
572
|
+
async function updateBubblesDeviceAttributes(options) {
|
|
573
|
+
const client = resolveDeviceClient(options);
|
|
574
|
+
try {
|
|
575
|
+
return await client.updateDeviceAttributes(
|
|
576
|
+
normalizeIdentifier(options.deviceId, "deviceId"),
|
|
577
|
+
normalizeDeviceAttributes(options.attributes),
|
|
578
|
+
withAppKeyHeader(options.appKey, options.requestOptions)
|
|
579
|
+
);
|
|
580
|
+
} catch (error) {
|
|
581
|
+
throwAppAuthenticationError(error);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
500
584
|
async function syncBubblesDevice(options) {
|
|
501
585
|
const client = resolveDeviceClient(options);
|
|
502
586
|
if (options.deviceId !== null) {
|
|
@@ -1279,6 +1363,7 @@ export {
|
|
|
1279
1363
|
extractDeviceIdFromDeviceResponse,
|
|
1280
1364
|
createBubblesDevice,
|
|
1281
1365
|
updateBubblesDevice,
|
|
1366
|
+
updateBubblesDeviceAttributes,
|
|
1282
1367
|
syncBubblesDevice,
|
|
1283
1368
|
BUBBLES_DELIVERY_STATUSES,
|
|
1284
1369
|
buildBubblesDeliveryStatusPayload,
|
package/dist/index.d.mts
CHANGED
|
@@ -93,10 +93,20 @@ interface SyncBubblesDeviceResult<TResponse = unknown> {
|
|
|
93
93
|
deviceId: string | null;
|
|
94
94
|
response: TResponse;
|
|
95
95
|
}
|
|
96
|
+
type BubblesDeviceAttributeValue = string | number | boolean | null | BubblesDeviceAttributeValue[] | {
|
|
97
|
+
[key: string]: BubblesDeviceAttributeValue;
|
|
98
|
+
};
|
|
99
|
+
type BubblesDeviceAttributes = Record<string, BubblesDeviceAttributeValue>;
|
|
100
|
+
interface UpdateBubblesDeviceAttributesOptions extends BubblesDeviceClientInput {
|
|
101
|
+
deviceId: string | number;
|
|
102
|
+
attributes: BubblesDeviceAttributes;
|
|
103
|
+
requestOptions?: RequestOptions;
|
|
104
|
+
}
|
|
96
105
|
declare function createBubblesDeviceClient(options: BubblesDeviceClientOptions): DeviceClient;
|
|
97
106
|
declare function extractDeviceIdFromDeviceResponse(value: unknown): string | null;
|
|
98
107
|
declare function createBubblesDevice<TResponse = unknown>(options: CreateBubblesDeviceOptions): Promise<TResponse>;
|
|
99
108
|
declare function updateBubblesDevice<TResponse = unknown>(options: UpdateBubblesDeviceOptions): Promise<TResponse>;
|
|
109
|
+
declare function updateBubblesDeviceAttributes<TResponse = unknown>(options: UpdateBubblesDeviceAttributesOptions): Promise<TResponse>;
|
|
100
110
|
declare function syncBubblesDevice<TResponse = unknown>(options: SyncBubblesDeviceOptions): Promise<SyncBubblesDeviceResult<TResponse>>;
|
|
101
111
|
|
|
102
112
|
type DeviceClientFactoryOptions = Omit<DeviceClientOptions, 'baseUrl'>;
|
|
@@ -220,6 +230,8 @@ declare function getDeviceRegistrationState(options?: GetDeviceTokenOptions): Pr
|
|
|
220
230
|
declare function getDeviceToken(options?: GetDeviceTokenOptions): Promise<DeviceTokenResult>;
|
|
221
231
|
declare function getFCMToken(options?: GetDeviceTokenOptions): Promise<string>;
|
|
222
232
|
|
|
233
|
+
declare function collectBubblesDeviceAttributes(): Promise<BubblesDeviceAttributes>;
|
|
234
|
+
|
|
223
235
|
interface RegisterDeviceOptions {
|
|
224
236
|
requestPermissions?: boolean;
|
|
225
237
|
permissionRequestOptions?: PermissionRequestOptions;
|
|
@@ -256,6 +268,7 @@ interface BubblesNotificationsProviderProps {
|
|
|
256
268
|
}
|
|
257
269
|
interface BubblesNotificationsContextValue {
|
|
258
270
|
registerDevice: (options?: RegisterDeviceOptions) => Promise<void>;
|
|
271
|
+
addDeviceAttribute: (name: string, value: BubblesDeviceAttributeValue) => Promise<void>;
|
|
259
272
|
deviceId: string | null;
|
|
260
273
|
pushToken: string | null;
|
|
261
274
|
tokenType: NativeTokenType | null;
|
|
@@ -303,4 +316,4 @@ interface BubblesNotificationPayload {
|
|
|
303
316
|
url: string | null;
|
|
304
317
|
}
|
|
305
318
|
|
|
306
|
-
export { ANDROID_CHANNEL_IMPORTANCE_VALUES, type AndroidChannelImportance, BUBBLES_DELIVERY_STATUSES, BUBBLES_NOTIFICATIONS_EXPO_EXTRA_KEY, BubblesAppAuthenticationError, type BubblesCreateDevicePayload, type BubblesDeliveryStatus, type BubblesDeliveryStatusPayload, type BubblesDevicePayload, type BubblesDevicePlatform, type BubblesForegroundNotificationPayloadContract, type BubblesNotificationDataContract, type BubblesNotificationPayload, type BubblesNotificationResponseEvent, type BubblesNotificationsContextValue, type BubblesNotificationsLogoutEvent, BubblesNotificationsProvider, type BubblesNotificationsProviderProps, type BubblesNotificationsRuntimeConfig, type BubblesSilentNotificationPayloadContract, type BubblesUpdateDevicePayload, type BubblesVisibleNotificationPayloadContract, type BuildBubblesCreateDevicePayloadOptions, type BuildBubblesDeliveryStatusPayloadOptions, type BuildBubblesDevicePayloadOptions, type CreateBubblesDeviceOptions, type CreateStoredDeliveryStatusEventOptions, type DefaultNotificationChannelConfig, type DeviceRegistrationState, type DeviceTokenResult, type EnsureDefaultNotificationChannelOptions, type ForegroundPresentationOptions, type GetDeviceTokenOptions, type GetNotificationPermissionsOptions, type NativeTokenType, type PermissionRequestOptions, type PostBubblesDeliveryStatusOptions, type PostStoredBubblesDeliveryStatusOptions, type RegisterDeviceOptions, type StoredDeliveryStatusEvent, type StoredDeliveryStatusLedger, type StoredDeviceState, type StoredDeviceStateUpdate, type SupportedPlatform, type SyncBubblesDeviceOptions, type SyncBubblesDeviceResult, type UpdateBubblesDeviceOptions, buildBubblesDeliveryStatusPayload, buildCreateDevicePayload, buildStoredDeliveryStatusEventKey, buildUpdateDevicePayload, createBubblesDevice, createBubblesDeviceClient, createStoredDeliveryStatusEvent, describeNotificationPermissionStatus, enqueueStoredDeliveryStatusEvent, ensureDefaultNotificationChannel, ensureNotificationPermissions, extractDeviceIdFromDeviceResponse, flushStoredBubblesDeliveryStatuses, getAndroidNotificationChannelImportance, getBubblesNotificationsRuntimeConfig, getDefaultNotificationChannelConfig, getDeviceRegistrationState, getDeviceToken, getFCMToken, getNotificationPermissions, isNotificationPermissionGranted, markStoredDeliveryStatusEventPosted, patchStoredDeviceState, postBubblesDeliveryStatus, postStoredBubblesDeliveryStatus, readStoredApiBaseUrl, readStoredAppKey, readStoredDeliveryStatusLedger, readStoredDeviceId, readStoredDeviceState, registerBubblesBackgroundHandlers, storeApiBaseUrl, storeAppKey, storeDeliveryStatusLedger, storeDeviceId, storeDeviceState, syncBubblesDevice, updateBubblesDevice, useBubblesNotifications };
|
|
319
|
+
export { ANDROID_CHANNEL_IMPORTANCE_VALUES, type AndroidChannelImportance, BUBBLES_DELIVERY_STATUSES, BUBBLES_NOTIFICATIONS_EXPO_EXTRA_KEY, BubblesAppAuthenticationError, type BubblesCreateDevicePayload, type BubblesDeliveryStatus, type BubblesDeliveryStatusPayload, type BubblesDeviceAttributeValue, type BubblesDeviceAttributes, type BubblesDevicePayload, type BubblesDevicePlatform, type BubblesForegroundNotificationPayloadContract, type BubblesNotificationDataContract, type BubblesNotificationPayload, type BubblesNotificationResponseEvent, type BubblesNotificationsContextValue, type BubblesNotificationsLogoutEvent, BubblesNotificationsProvider, type BubblesNotificationsProviderProps, type BubblesNotificationsRuntimeConfig, type BubblesSilentNotificationPayloadContract, type BubblesUpdateDevicePayload, type BubblesVisibleNotificationPayloadContract, type BuildBubblesCreateDevicePayloadOptions, type BuildBubblesDeliveryStatusPayloadOptions, type BuildBubblesDevicePayloadOptions, type CreateBubblesDeviceOptions, type CreateStoredDeliveryStatusEventOptions, type DefaultNotificationChannelConfig, type DeviceRegistrationState, type DeviceTokenResult, type EnsureDefaultNotificationChannelOptions, type ForegroundPresentationOptions, type GetDeviceTokenOptions, type GetNotificationPermissionsOptions, type NativeTokenType, type PermissionRequestOptions, type PostBubblesDeliveryStatusOptions, type PostStoredBubblesDeliveryStatusOptions, type RegisterDeviceOptions, type StoredDeliveryStatusEvent, type StoredDeliveryStatusLedger, type StoredDeviceState, type StoredDeviceStateUpdate, type SupportedPlatform, type SyncBubblesDeviceOptions, type SyncBubblesDeviceResult, type UpdateBubblesDeviceAttributesOptions, type UpdateBubblesDeviceOptions, buildBubblesDeliveryStatusPayload, buildCreateDevicePayload, buildStoredDeliveryStatusEventKey, buildUpdateDevicePayload, collectBubblesDeviceAttributes, createBubblesDevice, createBubblesDeviceClient, createStoredDeliveryStatusEvent, describeNotificationPermissionStatus, enqueueStoredDeliveryStatusEvent, ensureDefaultNotificationChannel, ensureNotificationPermissions, extractDeviceIdFromDeviceResponse, flushStoredBubblesDeliveryStatuses, getAndroidNotificationChannelImportance, getBubblesNotificationsRuntimeConfig, getDefaultNotificationChannelConfig, getDeviceRegistrationState, getDeviceToken, getFCMToken, getNotificationPermissions, isNotificationPermissionGranted, markStoredDeliveryStatusEventPosted, patchStoredDeviceState, postBubblesDeliveryStatus, postStoredBubblesDeliveryStatus, readStoredApiBaseUrl, readStoredAppKey, readStoredDeliveryStatusLedger, readStoredDeviceId, readStoredDeviceState, registerBubblesBackgroundHandlers, storeApiBaseUrl, storeAppKey, storeDeliveryStatusLedger, storeDeviceId, storeDeviceState, syncBubblesDevice, updateBubblesDevice, updateBubblesDeviceAttributes, useBubblesNotifications };
|
package/dist/index.d.ts
CHANGED
|
@@ -93,10 +93,20 @@ interface SyncBubblesDeviceResult<TResponse = unknown> {
|
|
|
93
93
|
deviceId: string | null;
|
|
94
94
|
response: TResponse;
|
|
95
95
|
}
|
|
96
|
+
type BubblesDeviceAttributeValue = string | number | boolean | null | BubblesDeviceAttributeValue[] | {
|
|
97
|
+
[key: string]: BubblesDeviceAttributeValue;
|
|
98
|
+
};
|
|
99
|
+
type BubblesDeviceAttributes = Record<string, BubblesDeviceAttributeValue>;
|
|
100
|
+
interface UpdateBubblesDeviceAttributesOptions extends BubblesDeviceClientInput {
|
|
101
|
+
deviceId: string | number;
|
|
102
|
+
attributes: BubblesDeviceAttributes;
|
|
103
|
+
requestOptions?: RequestOptions;
|
|
104
|
+
}
|
|
96
105
|
declare function createBubblesDeviceClient(options: BubblesDeviceClientOptions): DeviceClient;
|
|
97
106
|
declare function extractDeviceIdFromDeviceResponse(value: unknown): string | null;
|
|
98
107
|
declare function createBubblesDevice<TResponse = unknown>(options: CreateBubblesDeviceOptions): Promise<TResponse>;
|
|
99
108
|
declare function updateBubblesDevice<TResponse = unknown>(options: UpdateBubblesDeviceOptions): Promise<TResponse>;
|
|
109
|
+
declare function updateBubblesDeviceAttributes<TResponse = unknown>(options: UpdateBubblesDeviceAttributesOptions): Promise<TResponse>;
|
|
100
110
|
declare function syncBubblesDevice<TResponse = unknown>(options: SyncBubblesDeviceOptions): Promise<SyncBubblesDeviceResult<TResponse>>;
|
|
101
111
|
|
|
102
112
|
type DeviceClientFactoryOptions = Omit<DeviceClientOptions, 'baseUrl'>;
|
|
@@ -220,6 +230,8 @@ declare function getDeviceRegistrationState(options?: GetDeviceTokenOptions): Pr
|
|
|
220
230
|
declare function getDeviceToken(options?: GetDeviceTokenOptions): Promise<DeviceTokenResult>;
|
|
221
231
|
declare function getFCMToken(options?: GetDeviceTokenOptions): Promise<string>;
|
|
222
232
|
|
|
233
|
+
declare function collectBubblesDeviceAttributes(): Promise<BubblesDeviceAttributes>;
|
|
234
|
+
|
|
223
235
|
interface RegisterDeviceOptions {
|
|
224
236
|
requestPermissions?: boolean;
|
|
225
237
|
permissionRequestOptions?: PermissionRequestOptions;
|
|
@@ -256,6 +268,7 @@ interface BubblesNotificationsProviderProps {
|
|
|
256
268
|
}
|
|
257
269
|
interface BubblesNotificationsContextValue {
|
|
258
270
|
registerDevice: (options?: RegisterDeviceOptions) => Promise<void>;
|
|
271
|
+
addDeviceAttribute: (name: string, value: BubblesDeviceAttributeValue) => Promise<void>;
|
|
259
272
|
deviceId: string | null;
|
|
260
273
|
pushToken: string | null;
|
|
261
274
|
tokenType: NativeTokenType | null;
|
|
@@ -303,4 +316,4 @@ interface BubblesNotificationPayload {
|
|
|
303
316
|
url: string | null;
|
|
304
317
|
}
|
|
305
318
|
|
|
306
|
-
export { ANDROID_CHANNEL_IMPORTANCE_VALUES, type AndroidChannelImportance, BUBBLES_DELIVERY_STATUSES, BUBBLES_NOTIFICATIONS_EXPO_EXTRA_KEY, BubblesAppAuthenticationError, type BubblesCreateDevicePayload, type BubblesDeliveryStatus, type BubblesDeliveryStatusPayload, type BubblesDevicePayload, type BubblesDevicePlatform, type BubblesForegroundNotificationPayloadContract, type BubblesNotificationDataContract, type BubblesNotificationPayload, type BubblesNotificationResponseEvent, type BubblesNotificationsContextValue, type BubblesNotificationsLogoutEvent, BubblesNotificationsProvider, type BubblesNotificationsProviderProps, type BubblesNotificationsRuntimeConfig, type BubblesSilentNotificationPayloadContract, type BubblesUpdateDevicePayload, type BubblesVisibleNotificationPayloadContract, type BuildBubblesCreateDevicePayloadOptions, type BuildBubblesDeliveryStatusPayloadOptions, type BuildBubblesDevicePayloadOptions, type CreateBubblesDeviceOptions, type CreateStoredDeliveryStatusEventOptions, type DefaultNotificationChannelConfig, type DeviceRegistrationState, type DeviceTokenResult, type EnsureDefaultNotificationChannelOptions, type ForegroundPresentationOptions, type GetDeviceTokenOptions, type GetNotificationPermissionsOptions, type NativeTokenType, type PermissionRequestOptions, type PostBubblesDeliveryStatusOptions, type PostStoredBubblesDeliveryStatusOptions, type RegisterDeviceOptions, type StoredDeliveryStatusEvent, type StoredDeliveryStatusLedger, type StoredDeviceState, type StoredDeviceStateUpdate, type SupportedPlatform, type SyncBubblesDeviceOptions, type SyncBubblesDeviceResult, type UpdateBubblesDeviceOptions, buildBubblesDeliveryStatusPayload, buildCreateDevicePayload, buildStoredDeliveryStatusEventKey, buildUpdateDevicePayload, createBubblesDevice, createBubblesDeviceClient, createStoredDeliveryStatusEvent, describeNotificationPermissionStatus, enqueueStoredDeliveryStatusEvent, ensureDefaultNotificationChannel, ensureNotificationPermissions, extractDeviceIdFromDeviceResponse, flushStoredBubblesDeliveryStatuses, getAndroidNotificationChannelImportance, getBubblesNotificationsRuntimeConfig, getDefaultNotificationChannelConfig, getDeviceRegistrationState, getDeviceToken, getFCMToken, getNotificationPermissions, isNotificationPermissionGranted, markStoredDeliveryStatusEventPosted, patchStoredDeviceState, postBubblesDeliveryStatus, postStoredBubblesDeliveryStatus, readStoredApiBaseUrl, readStoredAppKey, readStoredDeliveryStatusLedger, readStoredDeviceId, readStoredDeviceState, registerBubblesBackgroundHandlers, storeApiBaseUrl, storeAppKey, storeDeliveryStatusLedger, storeDeviceId, storeDeviceState, syncBubblesDevice, updateBubblesDevice, useBubblesNotifications };
|
|
319
|
+
export { ANDROID_CHANNEL_IMPORTANCE_VALUES, type AndroidChannelImportance, BUBBLES_DELIVERY_STATUSES, BUBBLES_NOTIFICATIONS_EXPO_EXTRA_KEY, BubblesAppAuthenticationError, type BubblesCreateDevicePayload, type BubblesDeliveryStatus, type BubblesDeliveryStatusPayload, type BubblesDeviceAttributeValue, type BubblesDeviceAttributes, type BubblesDevicePayload, type BubblesDevicePlatform, type BubblesForegroundNotificationPayloadContract, type BubblesNotificationDataContract, type BubblesNotificationPayload, type BubblesNotificationResponseEvent, type BubblesNotificationsContextValue, type BubblesNotificationsLogoutEvent, BubblesNotificationsProvider, type BubblesNotificationsProviderProps, type BubblesNotificationsRuntimeConfig, type BubblesSilentNotificationPayloadContract, type BubblesUpdateDevicePayload, type BubblesVisibleNotificationPayloadContract, type BuildBubblesCreateDevicePayloadOptions, type BuildBubblesDeliveryStatusPayloadOptions, type BuildBubblesDevicePayloadOptions, type CreateBubblesDeviceOptions, type CreateStoredDeliveryStatusEventOptions, type DefaultNotificationChannelConfig, type DeviceRegistrationState, type DeviceTokenResult, type EnsureDefaultNotificationChannelOptions, type ForegroundPresentationOptions, type GetDeviceTokenOptions, type GetNotificationPermissionsOptions, type NativeTokenType, type PermissionRequestOptions, type PostBubblesDeliveryStatusOptions, type PostStoredBubblesDeliveryStatusOptions, type RegisterDeviceOptions, type StoredDeliveryStatusEvent, type StoredDeliveryStatusLedger, type StoredDeviceState, type StoredDeviceStateUpdate, type SupportedPlatform, type SyncBubblesDeviceOptions, type SyncBubblesDeviceResult, type UpdateBubblesDeviceAttributesOptions, type UpdateBubblesDeviceOptions, buildBubblesDeliveryStatusPayload, buildCreateDevicePayload, buildStoredDeliveryStatusEventKey, buildUpdateDevicePayload, collectBubblesDeviceAttributes, createBubblesDevice, createBubblesDeviceClient, createStoredDeliveryStatusEvent, describeNotificationPermissionStatus, enqueueStoredDeliveryStatusEvent, ensureDefaultNotificationChannel, ensureNotificationPermissions, extractDeviceIdFromDeviceResponse, flushStoredBubblesDeliveryStatuses, getAndroidNotificationChannelImportance, getBubblesNotificationsRuntimeConfig, getDefaultNotificationChannelConfig, getDeviceRegistrationState, getDeviceToken, getFCMToken, getNotificationPermissions, isNotificationPermissionGranted, markStoredDeliveryStatusEventPosted, patchStoredDeviceState, postBubblesDeliveryStatus, postStoredBubblesDeliveryStatus, readStoredApiBaseUrl, readStoredAppKey, readStoredDeliveryStatusLedger, readStoredDeviceId, readStoredDeviceState, registerBubblesBackgroundHandlers, storeApiBaseUrl, storeAppKey, storeDeliveryStatusLedger, storeDeviceId, storeDeviceState, syncBubblesDevice, updateBubblesDevice, updateBubblesDeviceAttributes, useBubblesNotifications };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
buildCreateDevicePayload: () => buildCreateDevicePayload,
|
|
40
40
|
buildStoredDeliveryStatusEventKey: () => buildStoredDeliveryStatusEventKey,
|
|
41
41
|
buildUpdateDevicePayload: () => buildUpdateDevicePayload,
|
|
42
|
+
collectBubblesDeviceAttributes: () => collectBubblesDeviceAttributes,
|
|
42
43
|
createBubblesDevice: () => createBubblesDevice,
|
|
43
44
|
createBubblesDeviceClient: () => createBubblesDeviceClient,
|
|
44
45
|
createStoredDeliveryStatusEvent: () => createStoredDeliveryStatusEvent,
|
|
@@ -73,6 +74,7 @@ __export(index_exports, {
|
|
|
73
74
|
storeDeviceState: () => storeDeviceState,
|
|
74
75
|
syncBubblesDevice: () => syncBubblesDevice,
|
|
75
76
|
updateBubblesDevice: () => updateBubblesDevice,
|
|
77
|
+
updateBubblesDeviceAttributes: () => updateBubblesDeviceAttributes,
|
|
76
78
|
useBubblesNotifications: () => useBubblesNotifications
|
|
77
79
|
});
|
|
78
80
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -535,6 +537,78 @@ function throwAppAuthenticationError(error) {
|
|
|
535
537
|
}
|
|
536
538
|
throw error;
|
|
537
539
|
}
|
|
540
|
+
function normalizeDeviceAttributes(value) {
|
|
541
|
+
return normalizeDeviceAttributeObject(value, "attributes", /* @__PURE__ */ new WeakSet());
|
|
542
|
+
}
|
|
543
|
+
function normalizeDeviceAttributeValue(value, sourceDescription, seenValues) {
|
|
544
|
+
if (value === null) {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
548
|
+
return value;
|
|
549
|
+
}
|
|
550
|
+
if (typeof value === "number") {
|
|
551
|
+
if (!Number.isFinite(value)) {
|
|
552
|
+
failWithBubblesError(`${sourceDescription} must be a finite number.`);
|
|
553
|
+
}
|
|
554
|
+
return value;
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(value)) {
|
|
557
|
+
if (seenValues.has(value)) {
|
|
558
|
+
failWithBubblesError(`${sourceDescription} must not contain cycles.`);
|
|
559
|
+
}
|
|
560
|
+
seenValues.add(value);
|
|
561
|
+
const normalizedValue = value.map(
|
|
562
|
+
(entry, index) => normalizeDeviceAttributeValue(
|
|
563
|
+
entry,
|
|
564
|
+
`${sourceDescription}[${index}]`,
|
|
565
|
+
seenValues
|
|
566
|
+
)
|
|
567
|
+
);
|
|
568
|
+
seenValues.delete(value);
|
|
569
|
+
return normalizedValue;
|
|
570
|
+
}
|
|
571
|
+
if (isPlainObject(value)) {
|
|
572
|
+
return normalizeDeviceAttributeObject(
|
|
573
|
+
value,
|
|
574
|
+
sourceDescription,
|
|
575
|
+
seenValues
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
failWithBubblesError(
|
|
579
|
+
`${sourceDescription} must be a JSON-compatible value.`
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
function normalizeDeviceAttributeObject(value, sourceDescription, seenValues) {
|
|
583
|
+
if (!isPlainObject(value)) {
|
|
584
|
+
failWithBubblesError('"attributes" must be an object.');
|
|
585
|
+
}
|
|
586
|
+
const valuePrototype = Object.getPrototypeOf(value);
|
|
587
|
+
if (valuePrototype !== Object.prototype && valuePrototype !== null) {
|
|
588
|
+
failWithBubblesError(
|
|
589
|
+
`${sourceDescription} must be a JSON-compatible object.`
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
593
|
+
failWithBubblesError(
|
|
594
|
+
`${sourceDescription} must not contain symbol keys.`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
if (seenValues.has(value)) {
|
|
598
|
+
failWithBubblesError(`${sourceDescription} must not contain cycles.`);
|
|
599
|
+
}
|
|
600
|
+
seenValues.add(value);
|
|
601
|
+
const normalizedValue = {};
|
|
602
|
+
for (const [attributeName, attributeValue] of Object.entries(value)) {
|
|
603
|
+
normalizedValue[attributeName] = normalizeDeviceAttributeValue(
|
|
604
|
+
attributeValue,
|
|
605
|
+
`${sourceDescription}.${attributeName}`,
|
|
606
|
+
seenValues
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
seenValues.delete(value);
|
|
610
|
+
return normalizedValue;
|
|
611
|
+
}
|
|
538
612
|
function extractDeviceIdFromDeviceResponse(value) {
|
|
539
613
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
540
614
|
return null;
|
|
@@ -574,6 +648,18 @@ async function updateBubblesDevice(options) {
|
|
|
574
648
|
throwAppAuthenticationError(error);
|
|
575
649
|
}
|
|
576
650
|
}
|
|
651
|
+
async function updateBubblesDeviceAttributes(options) {
|
|
652
|
+
const client = resolveDeviceClient(options);
|
|
653
|
+
try {
|
|
654
|
+
return await client.updateDeviceAttributes(
|
|
655
|
+
normalizeIdentifier(options.deviceId, "deviceId"),
|
|
656
|
+
normalizeDeviceAttributes(options.attributes),
|
|
657
|
+
withAppKeyHeader(options.appKey, options.requestOptions)
|
|
658
|
+
);
|
|
659
|
+
} catch (error) {
|
|
660
|
+
throwAppAuthenticationError(error);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
577
663
|
async function syncBubblesDevice(options) {
|
|
578
664
|
const client = resolveDeviceClient(options);
|
|
579
665
|
if (options.deviceId !== null) {
|
|
@@ -1392,6 +1478,73 @@ async function getFCMToken(options) {
|
|
|
1392
1478
|
return tokenResult.token;
|
|
1393
1479
|
}
|
|
1394
1480
|
|
|
1481
|
+
// src/runtime/device-attributes.ts
|
|
1482
|
+
var Application = __toESM(require("expo-application"));
|
|
1483
|
+
var Device = __toESM(require("expo-device"));
|
|
1484
|
+
function compactAttributes(attributes) {
|
|
1485
|
+
return Object.fromEntries(
|
|
1486
|
+
Object.entries(attributes).filter((entry) => entry[1] !== null && entry[1] !== void 0)
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
function addSection(attributes, name, section) {
|
|
1490
|
+
const compactedSection = compactAttributes(section);
|
|
1491
|
+
if (Object.keys(compactedSection).length > 0) {
|
|
1492
|
+
attributes[name] = compactedSection;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
function getDeviceTypeName(deviceType2) {
|
|
1496
|
+
switch (deviceType2) {
|
|
1497
|
+
case Device.DeviceType.PHONE:
|
|
1498
|
+
return "phone";
|
|
1499
|
+
case Device.DeviceType.TABLET:
|
|
1500
|
+
return "tablet";
|
|
1501
|
+
case Device.DeviceType.DESKTOP:
|
|
1502
|
+
return "desktop";
|
|
1503
|
+
case Device.DeviceType.TV:
|
|
1504
|
+
return "tv";
|
|
1505
|
+
case Device.DeviceType.UNKNOWN:
|
|
1506
|
+
return "unknown";
|
|
1507
|
+
default:
|
|
1508
|
+
return void 0;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
async function getDeviceType() {
|
|
1512
|
+
if (Device.deviceType !== null && Device.deviceType !== void 0) {
|
|
1513
|
+
return Device.deviceType;
|
|
1514
|
+
}
|
|
1515
|
+
try {
|
|
1516
|
+
return await Device.getDeviceTypeAsync();
|
|
1517
|
+
} catch {
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
async function collectBubblesDeviceAttributes() {
|
|
1522
|
+
const attributes = {};
|
|
1523
|
+
const deviceType2 = await getDeviceType();
|
|
1524
|
+
addSection(attributes, "device", {
|
|
1525
|
+
manufacturer: Device.manufacturer,
|
|
1526
|
+
brand: Device.brand,
|
|
1527
|
+
modelName: Device.modelName,
|
|
1528
|
+
modelId: typeof Device.modelId === "string" ? Device.modelId : void 0,
|
|
1529
|
+
deviceName: Device.deviceName,
|
|
1530
|
+
deviceType: getDeviceTypeName(deviceType2),
|
|
1531
|
+
totalMemory: Device.totalMemory
|
|
1532
|
+
});
|
|
1533
|
+
addSection(attributes, "os", {
|
|
1534
|
+
name: Device.osName,
|
|
1535
|
+
version: Device.osVersion,
|
|
1536
|
+
buildId: Device.osBuildId,
|
|
1537
|
+
platformApiLevel: Device.platformApiLevel
|
|
1538
|
+
});
|
|
1539
|
+
addSection(attributes, "app", {
|
|
1540
|
+
applicationId: Application.applicationId,
|
|
1541
|
+
applicationName: Application.applicationName,
|
|
1542
|
+
nativeApplicationVersion: Application.nativeApplicationVersion,
|
|
1543
|
+
nativeBuildVersion: Application.nativeBuildVersion
|
|
1544
|
+
});
|
|
1545
|
+
return attributes;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1395
1548
|
// src/runtime/BubblesNotificationsProvider.tsx
|
|
1396
1549
|
var import_react3 = require("react");
|
|
1397
1550
|
|
|
@@ -1635,6 +1788,20 @@ function buildRuntimeSnapshot(deviceId, registrationState) {
|
|
|
1635
1788
|
notificationsEnabled: registrationState.notificationsEnabled
|
|
1636
1789
|
};
|
|
1637
1790
|
}
|
|
1791
|
+
async function syncBubblesDeviceAttributesBestEffort(apiBaseUrl, appKey, deviceId) {
|
|
1792
|
+
try {
|
|
1793
|
+
const attributes = await collectBubblesDeviceAttributes();
|
|
1794
|
+
if (Object.keys(attributes).length > 0) {
|
|
1795
|
+
await updateBubblesDeviceAttributes({
|
|
1796
|
+
apiBaseUrl,
|
|
1797
|
+
appKey,
|
|
1798
|
+
deviceId,
|
|
1799
|
+
attributes
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
} catch {
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1638
1805
|
async function syncDeviceRegistrationState(options) {
|
|
1639
1806
|
const apiBaseUrl = getRequiredNonEmptyString3(options.apiBaseUrl, "apiBaseUrl");
|
|
1640
1807
|
const appKey = getRequiredNonEmptyString3(options.appKey, "appKey");
|
|
@@ -1661,6 +1828,13 @@ async function syncDeviceRegistrationState(options) {
|
|
|
1661
1828
|
});
|
|
1662
1829
|
const nextDeviceId = syncResult.deviceId ?? options.deviceId;
|
|
1663
1830
|
storeDeviceId(nextDeviceId);
|
|
1831
|
+
if (nextDeviceId) {
|
|
1832
|
+
await syncBubblesDeviceAttributesBestEffort(
|
|
1833
|
+
apiBaseUrl,
|
|
1834
|
+
appKey,
|
|
1835
|
+
nextDeviceId
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1664
1838
|
return {
|
|
1665
1839
|
...snapshot,
|
|
1666
1840
|
action: syncResult.action,
|
|
@@ -1848,6 +2022,7 @@ function BubblesNotificationsProvider(props) {
|
|
|
1848
2022
|
const [state, setState] = (0, import_react3.useState)(getInitialRuntimeState);
|
|
1849
2023
|
const { enqueueOperation, isMountedRef } = useBubblesRuntimeOperationQueue(setState);
|
|
1850
2024
|
const lastAutomaticSyncSignatureRef = (0, import_react3.useRef)(null);
|
|
2025
|
+
const currentDeviceIdRef = (0, import_react3.useRef)(state.deviceId);
|
|
1851
2026
|
const previousUserIdRef = (0, import_react3.useRef)(normalizedUserId);
|
|
1852
2027
|
const buildAutomaticSyncSignature = (0, import_react3.useCallback)((deviceId) => {
|
|
1853
2028
|
return getAutomaticSyncSignature({
|
|
@@ -1873,6 +2048,9 @@ function BubblesNotificationsProvider(props) {
|
|
|
1873
2048
|
clearBubblesForegroundPresentation();
|
|
1874
2049
|
};
|
|
1875
2050
|
}, [isMountedRef]);
|
|
2051
|
+
(0, import_react3.useEffect)(() => {
|
|
2052
|
+
currentDeviceIdRef.current = state.deviceId;
|
|
2053
|
+
}, [state.deviceId]);
|
|
1876
2054
|
(0, import_react3.useEffect)(() => {
|
|
1877
2055
|
let isCancelled = false;
|
|
1878
2056
|
void (async () => {
|
|
@@ -1940,6 +2118,7 @@ function BubblesNotificationsProvider(props) {
|
|
|
1940
2118
|
appVersion,
|
|
1941
2119
|
deviceId: storedDeviceId
|
|
1942
2120
|
});
|
|
2121
|
+
currentDeviceIdRef.current = result.deviceId;
|
|
1943
2122
|
return getRuntimeStateUpdate(result);
|
|
1944
2123
|
});
|
|
1945
2124
|
}, [
|
|
@@ -1973,9 +2152,10 @@ function BubblesNotificationsProvider(props) {
|
|
|
1973
2152
|
userId: normalizedUserId,
|
|
1974
2153
|
aliasing,
|
|
1975
2154
|
appVersion,
|
|
1976
|
-
deviceId:
|
|
2155
|
+
deviceId: currentDeviceIdRef.current,
|
|
1977
2156
|
registrationOptions: options
|
|
1978
2157
|
});
|
|
2158
|
+
currentDeviceIdRef.current = result.deviceId;
|
|
1979
2159
|
if (result.deviceId !== null) {
|
|
1980
2160
|
lastAutomaticSyncSignatureRef.current = buildAutomaticSyncSignature(
|
|
1981
2161
|
result.deviceId
|
|
@@ -1984,8 +2164,33 @@ function BubblesNotificationsProvider(props) {
|
|
|
1984
2164
|
return getRuntimeStateUpdate(result);
|
|
1985
2165
|
});
|
|
1986
2166
|
}
|
|
2167
|
+
function addDeviceAttribute(name, value) {
|
|
2168
|
+
return enqueueOperation(async () => {
|
|
2169
|
+
const attributeName = getRequiredNonEmptyString(name, "attributeName");
|
|
2170
|
+
if (value === void 0) {
|
|
2171
|
+
failWithBubblesError(
|
|
2172
|
+
'"addDeviceAttribute()" requires "value" to be a JSON-compatible value.'
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
const deviceId = currentDeviceIdRef.current;
|
|
2176
|
+
if (deviceId === null) {
|
|
2177
|
+
failWithBubblesError(
|
|
2178
|
+
'"addDeviceAttribute()" requires a backend "deviceId" before attributes can be synced.'
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
await updateBubblesDeviceAttributes({
|
|
2182
|
+
apiBaseUrl,
|
|
2183
|
+
appKey,
|
|
2184
|
+
deviceId,
|
|
2185
|
+
attributes: {
|
|
2186
|
+
[attributeName]: value
|
|
2187
|
+
}
|
|
2188
|
+
});
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
1987
2191
|
const contextValue = {
|
|
1988
2192
|
registerDevice,
|
|
2193
|
+
addDeviceAttribute,
|
|
1989
2194
|
deviceId: state.deviceId,
|
|
1990
2195
|
pushToken: state.pushToken,
|
|
1991
2196
|
tokenType: state.tokenType,
|
|
@@ -2022,6 +2227,7 @@ function useBubblesNotifications() {
|
|
|
2022
2227
|
buildCreateDevicePayload,
|
|
2023
2228
|
buildStoredDeliveryStatusEventKey,
|
|
2024
2229
|
buildUpdateDevicePayload,
|
|
2230
|
+
collectBubblesDeviceAttributes,
|
|
2025
2231
|
createBubblesDevice,
|
|
2026
2232
|
createBubblesDeviceClient,
|
|
2027
2233
|
createStoredDeliveryStatusEvent,
|
|
@@ -2056,5 +2262,6 @@ function useBubblesNotifications() {
|
|
|
2056
2262
|
storeDeviceState,
|
|
2057
2263
|
syncBubblesDevice,
|
|
2058
2264
|
updateBubblesDevice,
|
|
2265
|
+
updateBubblesDeviceAttributes,
|
|
2059
2266
|
useBubblesNotifications
|
|
2060
2267
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -47,8 +47,9 @@ import {
|
|
|
47
47
|
storeDeviceId,
|
|
48
48
|
storeDeviceState,
|
|
49
49
|
syncBubblesDevice,
|
|
50
|
-
updateBubblesDevice
|
|
51
|
-
|
|
50
|
+
updateBubblesDevice,
|
|
51
|
+
updateBubblesDeviceAttributes
|
|
52
|
+
} from "./chunk-3BAGWXHY.mjs";
|
|
52
53
|
|
|
53
54
|
// src/runtime/tokens.ts
|
|
54
55
|
import {
|
|
@@ -116,6 +117,73 @@ async function getFCMToken(options) {
|
|
|
116
117
|
return tokenResult.token;
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
// src/runtime/device-attributes.ts
|
|
121
|
+
import * as Application from "expo-application";
|
|
122
|
+
import * as Device from "expo-device";
|
|
123
|
+
function compactAttributes(attributes) {
|
|
124
|
+
return Object.fromEntries(
|
|
125
|
+
Object.entries(attributes).filter((entry) => entry[1] !== null && entry[1] !== void 0)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
function addSection(attributes, name, section) {
|
|
129
|
+
const compactedSection = compactAttributes(section);
|
|
130
|
+
if (Object.keys(compactedSection).length > 0) {
|
|
131
|
+
attributes[name] = compactedSection;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function getDeviceTypeName(deviceType2) {
|
|
135
|
+
switch (deviceType2) {
|
|
136
|
+
case Device.DeviceType.PHONE:
|
|
137
|
+
return "phone";
|
|
138
|
+
case Device.DeviceType.TABLET:
|
|
139
|
+
return "tablet";
|
|
140
|
+
case Device.DeviceType.DESKTOP:
|
|
141
|
+
return "desktop";
|
|
142
|
+
case Device.DeviceType.TV:
|
|
143
|
+
return "tv";
|
|
144
|
+
case Device.DeviceType.UNKNOWN:
|
|
145
|
+
return "unknown";
|
|
146
|
+
default:
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function getDeviceType() {
|
|
151
|
+
if (Device.deviceType !== null && Device.deviceType !== void 0) {
|
|
152
|
+
return Device.deviceType;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
return await Device.getDeviceTypeAsync();
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function collectBubblesDeviceAttributes() {
|
|
161
|
+
const attributes = {};
|
|
162
|
+
const deviceType2 = await getDeviceType();
|
|
163
|
+
addSection(attributes, "device", {
|
|
164
|
+
manufacturer: Device.manufacturer,
|
|
165
|
+
brand: Device.brand,
|
|
166
|
+
modelName: Device.modelName,
|
|
167
|
+
modelId: typeof Device.modelId === "string" ? Device.modelId : void 0,
|
|
168
|
+
deviceName: Device.deviceName,
|
|
169
|
+
deviceType: getDeviceTypeName(deviceType2),
|
|
170
|
+
totalMemory: Device.totalMemory
|
|
171
|
+
});
|
|
172
|
+
addSection(attributes, "os", {
|
|
173
|
+
name: Device.osName,
|
|
174
|
+
version: Device.osVersion,
|
|
175
|
+
buildId: Device.osBuildId,
|
|
176
|
+
platformApiLevel: Device.platformApiLevel
|
|
177
|
+
});
|
|
178
|
+
addSection(attributes, "app", {
|
|
179
|
+
applicationId: Application.applicationId,
|
|
180
|
+
applicationName: Application.applicationName,
|
|
181
|
+
nativeApplicationVersion: Application.nativeApplicationVersion,
|
|
182
|
+
nativeBuildVersion: Application.nativeBuildVersion
|
|
183
|
+
});
|
|
184
|
+
return attributes;
|
|
185
|
+
}
|
|
186
|
+
|
|
119
187
|
// src/runtime/BubblesNotificationsProvider.tsx
|
|
120
188
|
import { useCallback as useCallback2, useEffect, useRef as useRef2, useState } from "react";
|
|
121
189
|
|
|
@@ -362,6 +430,20 @@ function buildRuntimeSnapshot(deviceId, registrationState) {
|
|
|
362
430
|
notificationsEnabled: registrationState.notificationsEnabled
|
|
363
431
|
};
|
|
364
432
|
}
|
|
433
|
+
async function syncBubblesDeviceAttributesBestEffort(apiBaseUrl, appKey, deviceId) {
|
|
434
|
+
try {
|
|
435
|
+
const attributes = await collectBubblesDeviceAttributes();
|
|
436
|
+
if (Object.keys(attributes).length > 0) {
|
|
437
|
+
await updateBubblesDeviceAttributes({
|
|
438
|
+
apiBaseUrl,
|
|
439
|
+
appKey,
|
|
440
|
+
deviceId,
|
|
441
|
+
attributes
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
}
|
|
365
447
|
async function syncDeviceRegistrationState(options) {
|
|
366
448
|
const apiBaseUrl = getRequiredNonEmptyString2(options.apiBaseUrl, "apiBaseUrl");
|
|
367
449
|
const appKey = getRequiredNonEmptyString2(options.appKey, "appKey");
|
|
@@ -388,6 +470,13 @@ async function syncDeviceRegistrationState(options) {
|
|
|
388
470
|
});
|
|
389
471
|
const nextDeviceId = syncResult.deviceId ?? options.deviceId;
|
|
390
472
|
storeDeviceId(nextDeviceId);
|
|
473
|
+
if (nextDeviceId) {
|
|
474
|
+
await syncBubblesDeviceAttributesBestEffort(
|
|
475
|
+
apiBaseUrl,
|
|
476
|
+
appKey,
|
|
477
|
+
nextDeviceId
|
|
478
|
+
);
|
|
479
|
+
}
|
|
391
480
|
return {
|
|
392
481
|
...snapshot,
|
|
393
482
|
action: syncResult.action,
|
|
@@ -578,6 +667,7 @@ function BubblesNotificationsProvider(props) {
|
|
|
578
667
|
const [state, setState] = useState(getInitialRuntimeState);
|
|
579
668
|
const { enqueueOperation, isMountedRef } = useBubblesRuntimeOperationQueue(setState);
|
|
580
669
|
const lastAutomaticSyncSignatureRef = useRef2(null);
|
|
670
|
+
const currentDeviceIdRef = useRef2(state.deviceId);
|
|
581
671
|
const previousUserIdRef = useRef2(normalizedUserId);
|
|
582
672
|
const buildAutomaticSyncSignature = useCallback2((deviceId) => {
|
|
583
673
|
return getAutomaticSyncSignature({
|
|
@@ -603,6 +693,9 @@ function BubblesNotificationsProvider(props) {
|
|
|
603
693
|
clearBubblesForegroundPresentation();
|
|
604
694
|
};
|
|
605
695
|
}, [isMountedRef]);
|
|
696
|
+
useEffect(() => {
|
|
697
|
+
currentDeviceIdRef.current = state.deviceId;
|
|
698
|
+
}, [state.deviceId]);
|
|
606
699
|
useEffect(() => {
|
|
607
700
|
let isCancelled = false;
|
|
608
701
|
void (async () => {
|
|
@@ -670,6 +763,7 @@ function BubblesNotificationsProvider(props) {
|
|
|
670
763
|
appVersion,
|
|
671
764
|
deviceId: storedDeviceId
|
|
672
765
|
});
|
|
766
|
+
currentDeviceIdRef.current = result.deviceId;
|
|
673
767
|
return getRuntimeStateUpdate(result);
|
|
674
768
|
});
|
|
675
769
|
}, [
|
|
@@ -703,9 +797,10 @@ function BubblesNotificationsProvider(props) {
|
|
|
703
797
|
userId: normalizedUserId,
|
|
704
798
|
aliasing,
|
|
705
799
|
appVersion,
|
|
706
|
-
deviceId:
|
|
800
|
+
deviceId: currentDeviceIdRef.current,
|
|
707
801
|
registrationOptions: options
|
|
708
802
|
});
|
|
803
|
+
currentDeviceIdRef.current = result.deviceId;
|
|
709
804
|
if (result.deviceId !== null) {
|
|
710
805
|
lastAutomaticSyncSignatureRef.current = buildAutomaticSyncSignature(
|
|
711
806
|
result.deviceId
|
|
@@ -714,8 +809,33 @@ function BubblesNotificationsProvider(props) {
|
|
|
714
809
|
return getRuntimeStateUpdate(result);
|
|
715
810
|
});
|
|
716
811
|
}
|
|
812
|
+
function addDeviceAttribute(name, value) {
|
|
813
|
+
return enqueueOperation(async () => {
|
|
814
|
+
const attributeName = getRequiredNonEmptyString(name, "attributeName");
|
|
815
|
+
if (value === void 0) {
|
|
816
|
+
failWithBubblesError(
|
|
817
|
+
'"addDeviceAttribute()" requires "value" to be a JSON-compatible value.'
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
const deviceId = currentDeviceIdRef.current;
|
|
821
|
+
if (deviceId === null) {
|
|
822
|
+
failWithBubblesError(
|
|
823
|
+
'"addDeviceAttribute()" requires a backend "deviceId" before attributes can be synced.'
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
await updateBubblesDeviceAttributes({
|
|
827
|
+
apiBaseUrl,
|
|
828
|
+
appKey,
|
|
829
|
+
deviceId,
|
|
830
|
+
attributes: {
|
|
831
|
+
[attributeName]: value
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
});
|
|
835
|
+
}
|
|
717
836
|
const contextValue = {
|
|
718
837
|
registerDevice,
|
|
838
|
+
addDeviceAttribute,
|
|
719
839
|
deviceId: state.deviceId,
|
|
720
840
|
pushToken: state.pushToken,
|
|
721
841
|
tokenType: state.tokenType,
|
|
@@ -751,6 +871,7 @@ export {
|
|
|
751
871
|
buildCreateDevicePayload,
|
|
752
872
|
buildStoredDeliveryStatusEventKey,
|
|
753
873
|
buildUpdateDevicePayload,
|
|
874
|
+
collectBubblesDeviceAttributes,
|
|
754
875
|
createBubblesDevice,
|
|
755
876
|
createBubblesDeviceClient,
|
|
756
877
|
createStoredDeliveryStatusEvent,
|
|
@@ -785,5 +906,6 @@ export {
|
|
|
785
906
|
storeDeviceState,
|
|
786
907
|
syncBubblesDevice,
|
|
787
908
|
updateBubblesDevice,
|
|
909
|
+
updateBubblesDeviceAttributes,
|
|
788
910
|
useBubblesNotifications
|
|
789
911
|
};
|
package/dist/register-task.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishonfire/bubbles-expo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Expo config plugin, explicit background registration API, and React provider for Bubbles notifications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -68,8 +68,10 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
+
"expo-application": "~57.0.2",
|
|
71
72
|
"@fishonfire/bubbles-js": "0.1.0",
|
|
72
73
|
"expo-constants": "~57.0.16",
|
|
74
|
+
"expo-device": "~57.0.1",
|
|
73
75
|
"expo-file-system": "~57.0.6"
|
|
74
76
|
},
|
|
75
77
|
"devDependencies": {
|