Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
permissions:
id-token: write # Required for OIDC
contents: write
pull-requests: read # Required by transitively-called github-release.yml

jobs:
publish:
Expand Down
66 changes: 50 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ See the `examples/demo` directory for a full working example.

<docgen-index>

- [`initialize(...)`](#initialize)
- [`login(...)`](#login)
- [`logout()`](#logout)
- [`setConsentRequired(...)`](#setconsentrequired)
- [`setConsentGiven(...)`](#setconsentgiven)
- [Interfaces](#interfaces)
- [Type Aliases](#type-aliases)
* [`initialize(...)`](#initialize)
* [`login(...)`](#login)
* [`logout()`](#logout)
* [`setConsentRequired(...)`](#setconsentrequired)
* [`setConsentGiven(...)`](#setconsentgiven)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)

</docgen-index>

Expand All @@ -51,7 +51,8 @@ Initialize the SDK with your OneSignal app ID. Call during app startup.
| ----------- | ------------------- |
| **`appId`** | <code>string</code> |

---
--------------------


### login(...)

Expand All @@ -65,7 +66,8 @@ Log in to OneSignal as the user identified by `externalId`, switching the user c
| ---------------- | ------------------- |
| **`externalId`** | <code>string</code> |

---
--------------------


### logout()

Expand All @@ -75,7 +77,8 @@ logout() => Promise<void>

Log out the current user. The SDK will reference a new device-scoped user.

---
--------------------


### setConsentRequired(...)

Expand All @@ -89,7 +92,8 @@ Set whether user privacy consent is required before sending data to OneSignal. C
| -------------- | -------------------- |
| **`required`** | <code>boolean</code> |

---
--------------------


### setConsentGiven(...)

Expand All @@ -103,10 +107,12 @@ Indicate whether the user has granted privacy consent.
| ------------- | -------------------- |
| **`granted`** | <code>boolean</code> |

---
--------------------


### Interfaces


#### OneSignalDebugAPI

Debug helpers exposed via `OneSignal.Debug`.
Expand All @@ -116,6 +122,7 @@ Debug helpers exposed via `OneSignal.Debug`.
| **setLogLevel** | (logLevel: <a href="#loglevel">LogLevel</a>) =&gt; void | Set the log level printed to LogCat (Android) or the Xcode console (iOS). |
| **setAlertLevel** | (visualLogLevel: <a href="#loglevel">LogLevel</a>) =&gt; void | Set the log level shown to the user as alert dialogs. |


#### OneSignalUserAPI

Current-user operations exposed via `OneSignal.User`.
Expand Down Expand Up @@ -146,19 +153,22 @@ Current-user operations exposed via `OneSignal.User`.
| **getExternalId** | () =&gt; Promise&lt;string \| null&gt; | Get the external ID set via `login`, or null if the user is anonymous. |
| **trackEvent** | (name: string, properties?: object \| undefined) =&gt; Promise&lt;void&gt; | Track a custom event with an optional set of JSON-serializable properties. |


#### UserChangedState

| Prop | Type |
| ------------- | ----------------------------------------------- |
| **`current`** | <code><a href="#userstate">UserState</a></code> |


#### UserState

| Prop | Type |
| ----------------- | ------------------- |
| **`onesignalId`** | <code>string</code> |
| **`externalId`** | <code>string</code> |


#### OneSignalPushSubscriptionAPI

Push subscription state and controls exposed via `OneSignal.User.pushSubscription`.
Expand All @@ -173,13 +183,15 @@ Push subscription state and controls exposed via `OneSignal.User.pushSubscriptio
| **optIn** | () =&gt; Promise&lt;void&gt; | Opt the user in to push notifications. Prompts for permission if needed. |
| **optOut** | () =&gt; Promise&lt;void&gt; | Opt the user out of push notifications on this device. |


#### PushSubscriptionChangedState

| Prop | Type |
| -------------- | ----------------------------------------------------------------------- |
| **`previous`** | <code><a href="#pushsubscriptionstate">PushSubscriptionState</a></code> |
| **`current`** | <code><a href="#pushsubscriptionstate">PushSubscriptionState</a></code> |


#### PushSubscriptionState

| Prop | Type |
Expand All @@ -188,6 +200,7 @@ Push subscription state and controls exposed via `OneSignal.User.pushSubscriptio
| **`token`** | <code>string</code> |
| **`optedIn`** | <code>boolean</code> |


#### OneSignalNotificationsAPI

Notification permission and event handling exposed via `OneSignal.Notifications`.
Expand All @@ -205,20 +218,23 @@ Notification permission and event handling exposed via `OneSignal.Notifications`
| **removeNotification** | (id: number) =&gt; Promise&lt;void&gt; | Android only. Cancel a single notification by its Android notification ID. |
| **removeGroupedNotifications** | (id: string) =&gt; Promise&lt;void&gt; | Android only. Cancel a group of notifications by group key. |


#### NotificationClickEvent

| Prop | Type |
| ------------------ | --------------------------------------------------------------------------- |
| **`result`** | <code><a href="#notificationclickresult">NotificationClickResult</a></code> |
| **`notification`** | <code>OSNotification</code> |


#### NotificationClickResult

| Prop | Type |
| -------------- | ------------------- |
| **`actionId`** | <code>string</code> |
| **`url`** | <code>string</code> |


#### OneSignalInAppMessagesAPI

In-app message triggers and event handling exposed via `OneSignal.InAppMessages`.
Expand All @@ -235,19 +251,22 @@ In-app message triggers and event handling exposed via `OneSignal.InAppMessages`
| **setPaused** | (pause: boolean) =&gt; void | Pause or resume the display of in-app messages. |
| **getPaused** | () =&gt; Promise&lt;boolean&gt; | Whether in-app messaging is currently paused. |


#### InAppMessageClickEvent

| Prop | Type |
| ------------- | --------------------------------------------------------------------------- |
| **`message`** | <code><a href="#osinappmessage">OSInAppMessage</a></code> |
| **`result`** | <code><a href="#inappmessageclickresult">InAppMessageClickResult</a></code> |


#### OSInAppMessage

| Prop | Type |
| --------------- | ------------------- |
| **`messageId`** | <code>string</code> |


#### InAppMessageClickResult

| Prop | Type |
Expand All @@ -257,30 +276,35 @@ In-app message triggers and event handling exposed via `OneSignal.InAppMessages`
| **`url`** | <code>string</code> |
| **`urlTarget`** | <code><a href="#inappmessageactionurltype">InAppMessageActionUrlType</a></code> |


#### InAppMessageWillDisplayEvent

| Prop | Type |
| ------------- | --------------------------------------------------------- |
| **`message`** | <code><a href="#osinappmessage">OSInAppMessage</a></code> |


#### InAppMessageDidDisplayEvent

| Prop | Type |
| ------------- | --------------------------------------------------------- |
| **`message`** | <code><a href="#osinappmessage">OSInAppMessage</a></code> |


#### InAppMessageWillDismissEvent

| Prop | Type |
| ------------- | --------------------------------------------------------- |
| **`message`** | <code><a href="#osinappmessage">OSInAppMessage</a></code> |


#### InAppMessageDidDismissEvent

| Prop | Type |
| ------------- | --------------------------------------------------------- |
| **`message`** | <code><a href="#osinappmessage">OSInAppMessage</a></code> |


#### OneSignalSessionAPI

Outcome reporting exposed via `OneSignal.Session`.
Expand All @@ -291,6 +315,7 @@ Outcome reporting exposed via `OneSignal.Session`.
| **addUniqueOutcome** | (name: string) =&gt; Promise&lt;void&gt; | <a href="#record">Record</a> a unique outcome with the given name against the current session. |
| **addOutcomeWithValue** | (name: string, value: number) =&gt; Promise&lt;void&gt; | <a href="#record">Record</a> an outcome with the given name and value against the current session. |


#### OneSignalLocationAPI

Location permission and sharing exposed via `OneSignal.Location`.
Expand All @@ -301,6 +326,7 @@ Location permission and sharing exposed via `OneSignal.Location`.
| **setShared** | (shared: boolean) =&gt; void | Enable or disable sharing the device location with OneSignal. |
| **isShared** | () =&gt; Promise&lt;boolean&gt; | Whether the device location is currently shared with OneSignal. |


#### OneSignalLiveActivitiesAPI

Live activity controls exposed via `OneSignal.LiveActivities`. iOS only unless noted.
Expand All @@ -314,48 +340,56 @@ Live activity controls exposed via `OneSignal.LiveActivities`. iOS only unless n
| **setupDefault** | (options?: <a href="#liveactivitysetupoptions">LiveActivitySetupOptions</a> \| undefined) =&gt; Promise&lt;void&gt; | Set up the OneSignal default live activity, optionally enabling pushToStart/pushToUpdate. |
| **startDefault** | (activityId: string, attributes: <a href="#record">Record</a>&lt;string, unknown&gt;, content: <a href="#record">Record</a>&lt;string, unknown&gt;) =&gt; Promise&lt;void&gt; | Start a live activity backed by the OneSignal default attributes type. |


### Type Aliases


#### LogLevel

<code>(typeof <a href="#loglevel">LogLevel</a>)[keyof typeof LogLevel]</code>


#### Record

Construct a type with a set of properties K of type T

<code>{
[P in K]: T;
}</code>
<code>{ [P in K]: T; }</code>


#### OSNotificationPermission

<code>(typeof <a href="#osnotificationpermission">OSNotificationPermission</a>)[keyof typeof OSNotificationPermission]</code>


#### NotificationEventName

<code>'click' | 'foregroundWillDisplay' | 'permissionChange'</code>


#### NotificationEventTypeMap

<code>{ click: <a href="#notificationclickevent">NotificationClickEvent</a>; foregroundWillDisplay: NotificationWillDisplayEvent; permissionChange: boolean; }</code>


#### InAppMessageEventName

<code>'click' | 'willDisplay' | 'didDisplay' | 'willDismiss' | 'didDismiss'</code>


#### InAppMessageEventTypeMap

<code>{ click: <a href="#inappmessageclickevent">InAppMessageClickEvent</a>; willDisplay: <a href="#inappmessagewilldisplayevent">InAppMessageWillDisplayEvent</a>; didDisplay: <a href="#inappmessagediddisplayevent">InAppMessageDidDisplayEvent</a>; willDismiss: <a href="#inappmessagewilldismissevent">InAppMessageWillDismissEvent</a>; didDismiss: <a href="#inappmessagediddismissevent">InAppMessageDidDismissEvent</a>; }</code>


#### InAppMessageActionUrlType

<code>'browser' | 'webview' | 'replacement'</code>


#### LiveActivitySetupOptions

The setup options for `OneSignal.LiveActivities.setupDefault`.

<code>{ /** _ When true, OneSignal will listen for pushToStart tokens for the `OneSignalLiveActivityAttributes` structure. _/ enablePushToStart: boolean; /** _ When true, OneSignal will listen for pushToUpdate tokens for each start live activity that uses the _ `OneSignalLiveActivityAttributes` structure. \*/ enablePushToUpdate: boolean; }</code>
<code>{ /** * When true, OneSignal will listen for pushToStart tokens for the `OneSignalLiveActivityAttributes` structure. */ enablePushToStart: boolean; /** * When true, OneSignal will listen for pushToUpdate tokens for each start live activity that uses the * `OneSignalLiveActivityAttributes` structure. */ enablePushToUpdate: boolean; }</code>

</docgen-api>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class OneSignalCapacitorPlugin : Plugin(),
initialized = true

OneSignalWrapper.sdkType = "capacitor"
OneSignalWrapper.sdkVersion = "010000"
OneSignalWrapper.sdkVersion = "010002"
OneSignal.initWithContext(context, appId)

// If the SDK was initialized from a non-Activity context (FCM/work
Expand Down
22 changes: 15 additions & 7 deletions examples/demo/src/hooks/useOneSignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import OneSignal, {
type InAppMessageWillDisplayEvent,
type NotificationClickEvent,
type NotificationWillDisplayEvent,
type PushSubscriptionChangedState,
type UserChangedState,
} from '@onesignal/capacitor-plugin';
import { useCallback, useEffect, useRef, useState } from 'react';
Expand Down Expand Up @@ -200,16 +201,18 @@ export function useOneSignal(): UseOneSignalReturn {
e.getNotification().display();
};

const pushSubHandler = async () => {
const [id, optedIn] = await Promise.all([
OneSignal.User.pushSubscription.getIdAsync(),
OneSignal.User.pushSubscription.getOptedInAsync(),
]);
setPushSubscriptionId(id ?? undefined);
setIsPushEnabled(optedIn);
const pushSubHandler = (event: PushSubscriptionChangedState) => {
const { previous, current } = event;
const fmtToken = (t: string | undefined) => (t ? `${t.slice(0, 8)}…` : 'null');
console.log(
`Push subscription changed: id=${previous.id ?? 'null'} → ${current.id ?? 'null'}, optedIn=${previous.optedIn} → ${current.optedIn}, token=${fmtToken(previous.token)} → ${fmtToken(current.token)}`,
);
setPushSubscriptionId(current.id ?? undefined);
setIsPushEnabled(current.optedIn);
};

const permissionHandler = (granted: boolean) => {
console.log(`Permission changed: ${granted}`);
setHasNotificationPermission(granted);
};

Expand All @@ -224,6 +227,11 @@ export function useOneSignal(): UseOneSignalReturn {
};

const load = async () => {
// Uncomment if you want so you have time to see logs while trying to open
// safari web inspector. Not an issue for chrome web inspector.
// await new Promise((resolve) => setTimeout(resolve, 10_000));
// if (cancelled) return;

const nextAppId = resolveAppId();
const nextConsentRequired = preferences.getConsentRequired();
const nextPrivacyConsentGiven = preferences.getConsentGiven();
Expand Down
21 changes: 14 additions & 7 deletions examples/demo_pods/src/hooks/useOneSignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
type InAppMessageWillDisplayEvent,
type NotificationClickEvent,
type NotificationWillDisplayEvent,
type PushSubscriptionChangedState,
type UserChangedState,
} from '@onesignal/capacitor-plugin';
import { useCallback, useEffect, useRef, useState } from 'react';
Expand Down Expand Up @@ -200,16 +201,17 @@
e.getNotification().display();
};

const pushSubHandler = async () => {
const [id, optedIn] = await Promise.all([
OneSignal.User.pushSubscription.getIdAsync(),
OneSignal.User.pushSubscription.getOptedInAsync(),
]);
setPushSubscriptionId(id ?? undefined);
setIsPushEnabled(optedIn);
const pushSubHandler = (event: PushSubscriptionChangedState) => {
const { previous, current } = event;
const fmtToken = (t: string | undefined) => (t ? `${t.slice(0, 8)}…` : 'null');
console.log(
`Push subscription changed: id=${previous.id ?? 'null'} → ${current.id ?? 'null'}, optedIn=${previous.optedIn} → ${current.optedIn}, token=${fmtToken(previous.token)} → ${fmtToken(current.token)}`,
);
setPushSubscriptionId(current.id ?? undefined);
setIsPushEnabled(current.optedIn);
};

const permissionHandler = (granted: boolean) => {

Check warning on line 214 in examples/demo_pods/src/hooks/useOneSignal.ts

View check run for this annotation

Claude / Claude Code Review

Missing console.log for permissionHandler in demo_pods

Nit / pre-existing-style oversight: `examples/demo/src/hooks/useOneSignal.ts` adds `console.log(\`Permission changed: ${granted}\`)` inside `permissionHandler`, but the matching change was not made in `examples/demo_pods/src/hooks/useOneSignal.ts` at lines 213-215. The two example apps mirror each other in this PR (both received identical `pushSubHandler` refactors and identical 'Uncomment if you want…' comment blocks), so the missing log line in demo_pods looks like an oversight rather than int
Comment on lines 213 to 214

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit / pre-existing-style oversight: examples/demo/src/hooks/useOneSignal.ts adds console.log(\Permission changed: ${granted}`)insidepermissionHandler, but the matching change was not made in examples/demo_pods/src/hooks/useOneSignal.tsat lines 213-215. The two example apps mirror each other in this PR (both received identicalpushSubHandler` refactors and identical 'Uncomment if you want…' comment blocks), so the missing log line in demo_pods looks like an oversight rather than intentional. No functional impact — only affects example-app debug output.

Extended reasoning...

What the bug is

This PR makes a series of parallel changes to two example apps that are clearly meant to mirror each other: examples/demo/src/hooks/useOneSignal.ts and examples/demo_pods/src/hooks/useOneSignal.ts. In both files the PR rewrites pushSubHandler byte-identically (the same fmtToken helper, the same template literal, the same destructuring of previous/current) and adds the same 'Uncomment if you want so you have time to see logs while trying to open safari web inspector' comment block at the top of load(). However, the demo file also gets a new console.log(\Permission changed: ${granted}`)line insidepermissionHandler`, while demo_pods does not.

The specific code path

In examples/demo/src/hooks/useOneSignal.ts (around the changed lines), the handler now reads:

const permissionHandler = (granted: boolean) => {
  console.log(`Permission changed: ${granted}`);
  setHasNotificationPermission(granted);
};

In examples/demo_pods/src/hooks/useOneSignal.ts at lines 213-215, the same handler instead reads:

const permissionHandler = (granted: boolean) => {
  setHasNotificationPermission(granted);
};

Both handlers are wired up via OneSignal.Notifications.addEventListener('permissionChange', permissionHandler) later in the same effect, and the surrounding handlers (pushSubHandler, userChangeHandler, handleNotificationClick, handleForegroundWillDisplay, the IAM listeners) all have matching console.log calls in both files.

Why existing code doesn't prevent it

These are example apps, not part of the published SDK. There is no shared module between the two demos and no test that asserts they stay in sync, so a forgotten line in one of them passes lint, type-check, and CI.

Impact

Negligible at runtime — the missing log line only changes what the developer sees in the JS console while exercising demo_pods. It does not affect plugin behavior, packaged output, or end users in any way. This is purely a consistency/debugging-ergonomics nit.

Step-by-step proof

  1. Open examples/demo/src/hooks/useOneSignal.ts and find permissionHandler — it has two statements: a console.log and a state setter.
  2. Open examples/demo_pods/src/hooks/useOneSignal.ts and find permissionHandler at lines 213-215 — it has only the state setter.
  3. Compare the surrounding handlers in both files: pushSubHandler is byte-identical, including the exact Push subscription changed: id=… log message. userChangeHandler is byte-identical, including the User changed: onesignalId=… log. The IAM willDisplay/didDisplay/willDismiss/didDismiss/click handlers are all byte-identical with their console.log calls. Even the load() comment about Safari web inspector is byte-identical.
  4. Conclusion: the two files are intentionally kept in lockstep in this PR, but permissionHandler is the single divergence — a missed parallel edit.

How to fix

Add the same line to examples/demo_pods/src/hooks/useOneSignal.ts so its permissionHandler reads:

const permissionHandler = (granted: boolean) => {
  console.log(`Permission changed: ${granted}`);
  setHasNotificationPermission(granted);
};

One-line fix, no risk.

setHasNotificationPermission(granted);
};

Expand All @@ -224,6 +226,11 @@
};

const load = async () => {
// Uncomment if you want so you have time to see logs while trying to open
// safari web inspector. Not an issue for chrome web inspector.
// await new Promise((resolve) => setTimeout(resolve, 10_000));
// if (cancelled) return;

const nextAppId = resolveAppId();
const nextConsentRequired = preferences.getConsentRequired();
const nextPrivacyConsentGiven = preferences.getConsentGiven();
Expand Down
Loading
Loading