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: 0 additions & 1 deletion .github/actions/setup-demo/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,4 @@ runs:
run: |
echo "ONESIGNAL_APP_ID=${{ inputs.onesignal-app-id }}" > .env
echo "ONESIGNAL_API_KEY=${{ inputs.onesignal-api-key }}" >> .env
echo "E2E_MODE=true" >> .env
echo "ONESIGNAL_ANDROID_CHANNEL_ID=7ec2ece9-c538-4656-9516-1316f48a005c" >> .env
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dependencies {
// Exclude OkHttp from OneSignal's transitive deps: the otel module pulls in OkHttp 5.x
// (via opentelemetry-exporter-sender-okhttp) which is binary-incompatible with React Native's
// networking stack (okhttp3.internal.Util removed in 5.x). React Native already provides OkHttp 4.x.
api('com.onesignal:OneSignal:5.8.1') {
api('com.onesignal:OneSignal:5.9.2') {
Comment thread
fadi-george marked this conversation as resolved.
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public void invalidate() {
@Override
public void initialize(String appId) {
OneSignalWrapper.setSdkType("reactnative");
OneSignalWrapper.setSdkVersion("050405");
OneSignalWrapper.setSdkVersion("050406");

if (oneSignalInitDone) {
Logging.debug("Already initialized the OneSignal React-Native SDK", null);
Expand Down
1 change: 0 additions & 1 deletion examples/demo/.env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Default App ID (used if ONESIGNAL_APP_ID is empty): 77e32082-ea27-42e3-a898-c72e141824ef
ONESIGNAL_APP_ID=your-onesignal-app-id
ONESIGNAL_API_KEY=your-onesignal-api-key
E2E_MODE=false

# Optional: Android Notification Channel ID for the WITH SOUND test notification.
# Create one in your OneSignal dashboard under Settings > Android Notification Categories.
Expand Down
9 changes: 8 additions & 1 deletion examples/demo/src/components/SectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function SectionCard({ title, children, onInfoTap, sectionKey, st
{onInfoTap && (
<TouchableOpacity
onPress={onInfoTap}
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
style={styles.infoButton}
testID={sectionKey ? `${sectionKey}_info_icon` : undefined}
>
<Text style={styles.infoIcon}>ⓘ</Text>
Expand Down Expand Up @@ -50,6 +50,13 @@ const styles = StyleSheet.create({
letterSpacing: 0.5,
textTransform: 'uppercase',
},
infoButton: {
width: 32,
height: 32,
alignItems: 'center',
justifyContent: 'center',
marginRight: -11,
},
infoIcon: {
fontSize: 18,
color: AppColors.osGrey500,
Expand Down
3 changes: 1 addition & 2 deletions examples/demo/src/components/sections/AppSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import { View, Text, TouchableOpacity, Linking, StyleSheet } from 'react-native';

import { AppColors, AppTextStyles, AppTheme, AppSpacing } from '../../theme';
import { maskValue } from '../../utils/maskValue';
import SectionCard from '../SectionCard';
import ToggleRow from '../ToggleRow';

Expand Down Expand Up @@ -33,7 +32,7 @@ export default function AppSection({
ellipsizeMode="middle"
testID="app_id_value"
>
{maskValue(appId)}
{appId}
</Text>
</View>
</View>
Expand Down
3 changes: 1 addition & 2 deletions examples/demo/src/components/sections/PushSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

import { AppColors, AppTextStyles, AppTheme, AppSpacing } from '../../theme';
import { maskValue } from '../../utils/maskValue';
import ActionButton from '../ActionButton';
import SectionCard from '../SectionCard';
import ToggleRow from '../ToggleRow';
Expand Down Expand Up @@ -35,7 +34,7 @@ export default function PushSection({
ellipsizeMode="middle"
testID="push_id_value"
>
{maskValue(pushSubscriptionId ?? '—')}
{pushSubscriptionId ?? '—'}
</Text>
</View>
<View style={styles.divider} />
Expand Down
6 changes: 6 additions & 0 deletions examples/demo/src/hooks/useOneSignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type UseOneSignalReturn = {
consentRequired: boolean;
privacyConsentGiven: boolean;
externalUserId: string | undefined;
oneSignalId: string | undefined;
pushSubscriptionId: string | undefined;
isPushEnabled: boolean;
hasNotificationPermission: boolean;
Expand Down Expand Up @@ -115,6 +116,7 @@ function useOneSignalState(): UseOneSignalReturn {
const [consentRequired, setConsentRequiredState] = useState(false);
const [privacyConsentGiven, setPrivacyConsentGivenState] = useState(false);
const [externalUserId, setExternalUserId] = useState<string | undefined>(undefined);
const [oneSignalId, setOneSignalId] = useState<string | undefined>(undefined);
const [pushSubscriptionId, setPushSubscriptionId] = useState<string | undefined>(undefined);
const [isPushEnabled, setIsPushEnabled] = useState(false);
const [hasNotificationPermission, setHasNotificationPermission] = useState(false);
Expand Down Expand Up @@ -207,6 +209,8 @@ function useOneSignalState(): UseOneSignalReturn {
`User changed: onesignalId=${nextOnesignalId ?? 'null'}, externalId=${event.current.externalId ?? 'null'}`,
);

setOneSignalId(nextOnesignalId ?? undefined);

if (nextOnesignalId === null) {
return;
}
Expand Down Expand Up @@ -280,6 +284,7 @@ function useOneSignalState(): UseOneSignalReturn {
setIsReady(true);

const initialOnesignalId = await OneSignal.User.getOnesignalId();
setOneSignalId(initialOnesignalId ?? undefined);
if (initialOnesignalId) {
await fetchUserDataFromApi();
}
Expand Down Expand Up @@ -535,6 +540,7 @@ function useOneSignalState(): UseOneSignalReturn {
consentRequired,
privacyConsentGiven,
externalUserId,
oneSignalId,
pushSubscriptionId,
isPushEnabled,
hasNotificationPermission,
Comment thread
fadi-george marked this conversation as resolved.
Expand Down
5 changes: 3 additions & 2 deletions examples/demo/src/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ import { AppColors } from '../theme';
export default function HomeScreen() {
const navigation = useNavigation();
const os = useOneSignal();
const { isReady, promptPush } = os;

const [tooltipVisible, setTooltipVisible] = useState(false);
const [activeTooltip, setActiveTooltip] = useState<TooltipData | null>(null);

useEffect(() => {
if (os.isReady) os.promptPush();
}, [os.isReady, os.promptPush]);
if (isReady) promptPush();
}, [isReady, promptPush]);

const showTooltipModal = (key: string) => {
const tooltip = TooltipHelper.getInstance().getTooltip(key);
Expand Down
88 changes: 62 additions & 26 deletions examples/demo/src/services/OneSignalApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import { UserData, userDataFromJson } from '../models/UserData';

const DEFAULT_ANDROID_CHANNEL_ID = 'b3b015d9-c050-4042-8548-dcc34aa44aa4';

function isTransientSendFailure(data: unknown): boolean {
if (!data || typeof data !== 'object') return false;
const record = data as { id?: unknown; errors?: unknown; recipients?: unknown };
const errors = record.errors;
const hasErrors =
(Array.isArray(errors) && errors.length > 0) ||
(errors != null && typeof errors === 'object' && Object.keys(errors).length > 0);
const missingId = typeof record.id !== 'string' || record.id.length === 0;
const zeroRecipients = typeof record.recipients === 'number' && record.recipients === 0;
return hasErrors || missingId || zeroRecipients;
}

class OneSignalApiService {
private static _instance: OneSignalApiService;
private _appId: string = '';
Expand Down Expand Up @@ -75,35 +87,59 @@ class OneSignalApiService {
subscriptionId: string,
extra: Record<string, unknown>,
): Promise<boolean> {
try {
const body = {
app_id: this._appId,
include_subscription_ids: [subscriptionId],
headings,
contents,
...extra,
};

const response = await fetch('https://onesignal.com/api/v1/notifications', {
method: 'POST',
headers: {
Accept: 'application/vnd.onesignal.v1+json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});

if (!response.ok) {
const text = await response.text();
console.error(`Send notification failed: ${text}`);
const body = {
app_id: this._appId,
include_subscription_ids: [subscriptionId],
headings,
contents,
...extra,
};

const maxAttempts = 3;

// Retry while the OneSignal backend hasn't yet indexed the freshly
// created subscription. The /notifications endpoint reports this race in
// a few different shapes, all of which return HTTP 200:
// {"id":"...","recipients":0} (user just switched, push token not yet attached)
// {"id":"...","errors":{"invalid_player_ids":[...]}}
// {"id":"","errors":["All included players are not subscribed"]}
// {"id":"","errors":[...]}
// Treat any 200 response with no real id, populated errors, or recipients=0 as transient.
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const response = await fetch('https://onesignal.com/api/v1/notifications', {
method: 'POST',
headers: {
Accept: 'application/vnd.onesignal.v1+json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});

if (!response.ok) {
const text = await response.text();
console.error(`Send notification failed: ${text}`);
return false;
}

const data = await response.json().catch(() => undefined);
if (isTransientSendFailure(data)) {
if (attempt < maxAttempts) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 3_000 * attempt));
continue;
}
console.error(`Send notification failed: ${JSON.stringify(data)}`);
return false;
}

return true;
} catch (err) {
console.error(`Send notification error: ${String(err)}`);
return false;
}

return true;
} catch (err) {
console.error(`Send notification error: ${String(err)}`);
return false;
}

return false;
}

async updateLiveActivity(
Expand Down
10 changes: 0 additions & 10 deletions examples/demo/src/utils/maskValue.ts

This file was deleted.

1 change: 0 additions & 1 deletion examples/demo/types/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ declare module '@env' {
export const ONESIGNAL_APP_ID: string;
export const ONESIGNAL_API_KEY: string;
export const ONESIGNAL_ANDROID_CHANNEL_ID: string;
export const E2E_MODE: string;
}
2 changes: 1 addition & 1 deletion ios/RCTOneSignal/RCTOneSignal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ - (void)initOneSignal:(NSDictionary *)launchOptions {
return;

OneSignalWrapper.sdkType = @"reactnative";
OneSignalWrapper.sdkVersion = @"050405";
OneSignalWrapper.sdkVersion = @"050406";
// initialize the SDK with a nil app ID so cold start click listeners can be
// triggered
[OneSignal initialize:nil withLaunchOptions:launchOptions];
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-onesignal",
"version": "5.4.5",
"version": "5.4.6",
Comment thread
fadi-george marked this conversation as resolved.
"description": "React Native OneSignal SDK",
"keywords": [
"android",
Expand Down
Loading