Skip to content

Commit 180b5cd

Browse files
onesignal-deploygithub-actions[bot]fadi-georgecursoragent
authored
chore: Release 5.4.6 (#1948)
Co-authored-by: github-actions[bot] <noreply@onesignal.com> Co-authored-by: Fadi George <fadii925@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 69bbe68 commit 180b5cd

14 files changed

Lines changed: 85 additions & 50 deletions

File tree

.github/actions/setup-demo/action.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,4 @@ runs:
5454
run: |
5555
echo "ONESIGNAL_APP_ID=${{ inputs.onesignal-app-id }}" > .env
5656
echo "ONESIGNAL_API_KEY=${{ inputs.onesignal-api-key }}" >> .env
57-
echo "E2E_MODE=true" >> .env
5857
echo "ONESIGNAL_ANDROID_CHANNEL_ID=7ec2ece9-c538-4656-9516-1316f48a005c" >> .env

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ dependencies {
3939
// Exclude OkHttp from OneSignal's transitive deps: the otel module pulls in OkHttp 5.x
4040
// (via opentelemetry-exporter-sender-okhttp) which is binary-incompatible with React Native's
4141
// networking stack (okhttp3.internal.Util removed in 5.x). React Native already provides OkHttp 4.x.
42-
api('com.onesignal:OneSignal:5.8.1') {
42+
api('com.onesignal:OneSignal:5.9.2') {
4343
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
4444
}
4545

android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public void invalidate() {
230230
@Override
231231
public void initialize(String appId) {
232232
OneSignalWrapper.setSdkType("reactnative");
233-
OneSignalWrapper.setSdkVersion("050405");
233+
OneSignalWrapper.setSdkVersion("050406");
234234

235235
if (oneSignalInitDone) {
236236
Logging.debug("Already initialized the OneSignal React-Native SDK", null);

examples/demo/.env.example

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Default App ID (used if ONESIGNAL_APP_ID is empty): 77e32082-ea27-42e3-a898-c72e141824ef
22
ONESIGNAL_APP_ID=your-onesignal-app-id
33
ONESIGNAL_API_KEY=your-onesignal-api-key
4-
E2E_MODE=false
54

65
# Optional: Android Notification Channel ID for the WITH SOUND test notification.
76
# Create one in your OneSignal dashboard under Settings > Android Notification Categories.

examples/demo/src/components/SectionCard.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default function SectionCard({ title, children, onInfoTap, sectionKey, st
1919
{onInfoTap && (
2020
<TouchableOpacity
2121
onPress={onInfoTap}
22-
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
22+
style={styles.infoButton}
2323
testID={sectionKey ? `${sectionKey}_info_icon` : undefined}
2424
>
2525
<Text style={styles.infoIcon}></Text>
@@ -50,6 +50,13 @@ const styles = StyleSheet.create({
5050
letterSpacing: 0.5,
5151
textTransform: 'uppercase',
5252
},
53+
infoButton: {
54+
width: 32,
55+
height: 32,
56+
alignItems: 'center',
57+
justifyContent: 'center',
58+
marginRight: -11,
59+
},
5360
infoIcon: {
5461
fontSize: 18,
5562
color: AppColors.osGrey500,

examples/demo/src/components/sections/AppSection.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from 'react';
22
import { View, Text, TouchableOpacity, Linking, StyleSheet } from 'react-native';
33

44
import { AppColors, AppTextStyles, AppTheme, AppSpacing } from '../../theme';
5-
import { maskValue } from '../../utils/maskValue';
65
import SectionCard from '../SectionCard';
76
import ToggleRow from '../ToggleRow';
87

@@ -33,7 +32,7 @@ export default function AppSection({
3332
ellipsizeMode="middle"
3433
testID="app_id_value"
3534
>
36-
{maskValue(appId)}
35+
{appId}
3736
</Text>
3837
</View>
3938
</View>

examples/demo/src/components/sections/PushSection.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from 'react';
22
import { View, Text, StyleSheet } from 'react-native';
33

44
import { AppColors, AppTextStyles, AppTheme, AppSpacing } from '../../theme';
5-
import { maskValue } from '../../utils/maskValue';
65
import ActionButton from '../ActionButton';
76
import SectionCard from '../SectionCard';
87
import ToggleRow from '../ToggleRow';
@@ -35,7 +34,7 @@ export default function PushSection({
3534
ellipsizeMode="middle"
3635
testID="push_id_value"
3736
>
38-
{maskValue(pushSubscriptionId ?? '—')}
37+
{pushSubscriptionId ?? '—'}
3938
</Text>
4039
</View>
4140
<View style={styles.divider} />

examples/demo/src/hooks/useOneSignal.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export type UseOneSignalReturn = {
6262
consentRequired: boolean;
6363
privacyConsentGiven: boolean;
6464
externalUserId: string | undefined;
65+
oneSignalId: string | undefined;
6566
pushSubscriptionId: string | undefined;
6667
isPushEnabled: boolean;
6768
hasNotificationPermission: boolean;
@@ -115,6 +116,7 @@ function useOneSignalState(): UseOneSignalReturn {
115116
const [consentRequired, setConsentRequiredState] = useState(false);
116117
const [privacyConsentGiven, setPrivacyConsentGivenState] = useState(false);
117118
const [externalUserId, setExternalUserId] = useState<string | undefined>(undefined);
119+
const [oneSignalId, setOneSignalId] = useState<string | undefined>(undefined);
118120
const [pushSubscriptionId, setPushSubscriptionId] = useState<string | undefined>(undefined);
119121
const [isPushEnabled, setIsPushEnabled] = useState(false);
120122
const [hasNotificationPermission, setHasNotificationPermission] = useState(false);
@@ -207,6 +209,8 @@ function useOneSignalState(): UseOneSignalReturn {
207209
`User changed: onesignalId=${nextOnesignalId ?? 'null'}, externalId=${event.current.externalId ?? 'null'}`,
208210
);
209211

212+
setOneSignalId(nextOnesignalId ?? undefined);
213+
210214
if (nextOnesignalId === null) {
211215
return;
212216
}
@@ -280,6 +284,7 @@ function useOneSignalState(): UseOneSignalReturn {
280284
setIsReady(true);
281285

282286
const initialOnesignalId = await OneSignal.User.getOnesignalId();
287+
setOneSignalId(initialOnesignalId ?? undefined);
283288
if (initialOnesignalId) {
284289
await fetchUserDataFromApi();
285290
}
@@ -535,6 +540,7 @@ function useOneSignalState(): UseOneSignalReturn {
535540
consentRequired,
536541
privacyConsentGiven,
537542
externalUserId,
543+
oneSignalId,
538544
pushSubscriptionId,
539545
isPushEnabled,
540546
hasNotificationPermission,

examples/demo/src/screens/HomeScreen.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ import { AppColors } from '../theme';
2828
export default function HomeScreen() {
2929
const navigation = useNavigation();
3030
const os = useOneSignal();
31+
const { isReady, promptPush } = os;
3132

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

3536
useEffect(() => {
36-
if (os.isReady) os.promptPush();
37-
}, [os.isReady, os.promptPush]);
37+
if (isReady) promptPush();
38+
}, [isReady, promptPush]);
3839

3940
const showTooltipModal = (key: string) => {
4041
const tooltip = TooltipHelper.getInstance().getTooltip(key);

examples/demo/src/services/OneSignalApiService.ts

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ import { UserData, userDataFromJson } from '../models/UserData';
55

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

8+
function isTransientSendFailure(data: unknown): boolean {
9+
if (!data || typeof data !== 'object') return false;
10+
const record = data as { id?: unknown; errors?: unknown; recipients?: unknown };
11+
const errors = record.errors;
12+
const hasErrors =
13+
(Array.isArray(errors) && errors.length > 0) ||
14+
(errors != null && typeof errors === 'object' && Object.keys(errors).length > 0);
15+
const missingId = typeof record.id !== 'string' || record.id.length === 0;
16+
const zeroRecipients = typeof record.recipients === 'number' && record.recipients === 0;
17+
return hasErrors || missingId || zeroRecipients;
18+
}
19+
820
class OneSignalApiService {
921
private static _instance: OneSignalApiService;
1022
private _appId: string = '';
@@ -75,35 +87,59 @@ class OneSignalApiService {
7587
subscriptionId: string,
7688
extra: Record<string, unknown>,
7789
): Promise<boolean> {
78-
try {
79-
const body = {
80-
app_id: this._appId,
81-
include_subscription_ids: [subscriptionId],
82-
headings,
83-
contents,
84-
...extra,
85-
};
86-
87-
const response = await fetch('https://onesignal.com/api/v1/notifications', {
88-
method: 'POST',
89-
headers: {
90-
Accept: 'application/vnd.onesignal.v1+json',
91-
'Content-Type': 'application/json',
92-
},
93-
body: JSON.stringify(body),
94-
});
95-
96-
if (!response.ok) {
97-
const text = await response.text();
98-
console.error(`Send notification failed: ${text}`);
90+
const body = {
91+
app_id: this._appId,
92+
include_subscription_ids: [subscriptionId],
93+
headings,
94+
contents,
95+
...extra,
96+
};
97+
98+
const maxAttempts = 3;
99+
100+
// Retry while the OneSignal backend hasn't yet indexed the freshly
101+
// created subscription. The /notifications endpoint reports this race in
102+
// a few different shapes, all of which return HTTP 200:
103+
// {"id":"...","recipients":0} (user just switched, push token not yet attached)
104+
// {"id":"...","errors":{"invalid_player_ids":[...]}}
105+
// {"id":"","errors":["All included players are not subscribed"]}
106+
// {"id":"","errors":[...]}
107+
// Treat any 200 response with no real id, populated errors, or recipients=0 as transient.
108+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
109+
try {
110+
const response = await fetch('https://onesignal.com/api/v1/notifications', {
111+
method: 'POST',
112+
headers: {
113+
Accept: 'application/vnd.onesignal.v1+json',
114+
'Content-Type': 'application/json',
115+
},
116+
body: JSON.stringify(body),
117+
});
118+
119+
if (!response.ok) {
120+
const text = await response.text();
121+
console.error(`Send notification failed: ${text}`);
122+
return false;
123+
}
124+
125+
const data = await response.json().catch(() => undefined);
126+
if (isTransientSendFailure(data)) {
127+
if (attempt < maxAttempts) {
128+
await new Promise<void>((resolve) => setTimeout(() => resolve(), 3_000 * attempt));
129+
continue;
130+
}
131+
console.error(`Send notification failed: ${JSON.stringify(data)}`);
132+
return false;
133+
}
134+
135+
return true;
136+
} catch (err) {
137+
console.error(`Send notification error: ${String(err)}`);
99138
return false;
100139
}
101-
102-
return true;
103-
} catch (err) {
104-
console.error(`Send notification error: ${String(err)}`);
105-
return false;
106140
}
141+
142+
return false;
107143
}
108144

109145
async updateLiveActivity(

0 commit comments

Comments
 (0)