Skip to content

Commit 5e08c5e

Browse files
committed
chore(demo): make Android channel ID configurable
chore(demo): replace en dash with em dash fix(demo): retry on invalid_player_ids
1 parent 0e7ab0a commit 5e08c5e

4 files changed

Lines changed: 90 additions & 19 deletions

File tree

examples/demo/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
ONESIGNAL_APP_ID=your-onesignal-app-id
33
ONESIGNAL_API_KEY=your_rest_api_key
44
E2E_MODE=false
5+
6+
# Optional: Android Notification Channel ID for the WITH SOUND test notification.
7+
# Create one in your OneSignal dashboard under Settings > Android Notification Categories.
8+
ONESIGNAL_ANDROID_CHANNEL_ID=

examples/demo/Controls/Sections/UserSection.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
<Label
4141
Grid.Column="1"
4242
x:Name="ExternalIdLabel"
43-
Text=""
43+
Text=""
4444
Style="{StaticResource BodySmallStyle}"
4545
FontFamily="DroidSansMono"
4646
VerticalOptions="Center"

examples/demo/Services/OneSignalApiService.cs

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ namespace OneSignalDemo.Services;
66

77
public class OneSignalApiService
88
{
9+
private const string DefaultAndroidChannelId = "b3b015d9-c050-4042-8548-dcc34aa44aa4";
10+
911
private string _appId = "";
1012

1113
public void SetAppId(string appId) => _appId = appId;
@@ -20,6 +22,12 @@ public bool HasApiKey()
2022

2123
private string GetApiKey() => DotEnv.Get("ONESIGNAL_API_KEY");
2224

25+
private static string GetAndroidChannelId()
26+
{
27+
var value = DotEnv.Get("ONESIGNAL_ANDROID_CHANNEL_ID")?.Trim();
28+
return string.IsNullOrEmpty(value) ? DefaultAndroidChannelId : value;
29+
}
30+
2331
public async Task<bool> SendNotificationAsync(NotificationType type, string subscriptionId)
2432
{
2533
try
@@ -55,7 +63,7 @@ public async Task<bool> SendNotificationAsync(NotificationType type, string subs
5563
extra = new Dictionary<string, object>
5664
{
5765
["ios_sound"] = "vine_boom.wav",
58-
["android_channel_id"] = "b3b015d9-c050-4042-8548-dcc34aa44aa4",
66+
["android_channel_id"] = GetAndroidChannelId(),
5967
};
6068
break;
6169
default:
@@ -111,12 +119,61 @@ private async Task<bool> SendAsync(
111119
}
112120

113121
var json = JsonSerializer.Serialize(payload);
114-
var content = new StringContent(json, Encoding.UTF8, "application/json");
115-
var response = await client.PostAsync(
116-
"https://onesignal.com/api/v1/notifications",
117-
content
118-
);
119-
return response.IsSuccessStatusCode;
122+
123+
// Retry once on `invalid_player_ids` to absorb the brief race where the
124+
// subscription has been created locally but is not yet visible to the
125+
// /notifications endpoint.
126+
for (var attempt = 0; attempt < 2; attempt++)
127+
{
128+
var content = new StringContent(json, Encoding.UTF8, "application/json");
129+
var response = await client.PostAsync(
130+
"https://onesignal.com/api/v1/notifications",
131+
content
132+
);
133+
134+
if (!response.IsSuccessStatusCode)
135+
return false;
136+
137+
var responseJson = await response.Content.ReadAsStringAsync();
138+
if (HasInvalidPlayerIds(responseJson))
139+
{
140+
if (attempt == 0)
141+
{
142+
await Task.Delay(3000);
143+
continue;
144+
}
145+
return false;
146+
}
147+
148+
return true;
149+
}
150+
151+
return false;
152+
}
153+
154+
private static bool HasInvalidPlayerIds(string responseJson)
155+
{
156+
if (string.IsNullOrWhiteSpace(responseJson))
157+
return false;
158+
try
159+
{
160+
using var doc = JsonDocument.Parse(responseJson);
161+
if (
162+
doc.RootElement.ValueKind == JsonValueKind.Object
163+
&& doc.RootElement.TryGetProperty("errors", out var errors)
164+
&& errors.ValueKind == JsonValueKind.Object
165+
&& errors.TryGetProperty("invalid_player_ids", out var invalidIds)
166+
&& invalidIds.ValueKind == JsonValueKind.Array
167+
)
168+
{
169+
return invalidIds.GetArrayLength() > 0;
170+
}
171+
}
172+
catch
173+
{
174+
// Ignore malformed bodies; treat as success since status was 2xx.
175+
}
176+
return false;
120177
}
121178

122179
public async Task<UserData?> FetchUserAsync(string onesignalId)

examples/demo/ViewModels/AppViewModel.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public partial class AppViewModel : ObservableObject
2929
private string _userStatus = "Anonymous";
3030

3131
[ObservableProperty]
32-
private string _externalIdDisplay = "";
32+
private string _externalIdDisplay = "";
3333

3434
[ObservableProperty]
3535
private bool _isLoggedIn;
@@ -39,7 +39,7 @@ public partial class AppViewModel : ObservableObject
3939

4040
// Push section
4141
[ObservableProperty]
42-
private string _pushSubscriptionId = "";
42+
private string _pushSubscriptionId = "";
4343

4444
[ObservableProperty]
4545
private bool _isPushEnabled;
@@ -106,12 +106,18 @@ public AppViewModel(PreferencesService prefs, OneSignalApiService apiService)
106106
}
107107

108108
private static string MaskValue(string value) =>
109-
string.IsNullOrEmpty(value) ? value : new string('\u2022', value.Length);
109+
IsE2EMode && value != "—" ? new string('\u2022', value.Length) : value;
110+
111+
private static string MaskPushId(string? value, bool hasNotificationPermission) =>
112+
hasNotificationPermission ? MaskValue(string.IsNullOrEmpty(value) ? "—" : value) : "—";
113+
114+
private static string FormatToken(string? value) =>
115+
string.IsNullOrEmpty(value) ? "null" : $"{value[..Math.Min(8, value.Length)]}...";
110116

111117
public async Task LoadInitialStateAsync()
112118
{
113119
var rawAppId = _apiService.GetAppId();
114-
AppId = IsE2EMode ? MaskValue(rawAppId) : rawAppId;
120+
AppId = MaskValue(rawAppId);
115121
ConsentRequired = _prefs.ConsentRequired;
116122
PrivacyConsentGiven = _prefs.PrivacyConsent;
117123
InAppMessagesPaused = OneSignal.InAppMessages.Paused;
@@ -121,8 +127,8 @@ public async Task LoadInitialStateAsync()
121127
var extId = OneSignal.User.ExternalId ?? _prefs.ExternalUserId;
122128
UpdateUserStatus(extId);
123129

124-
var rawPushId = OneSignal.User.PushSubscription.Id ?? "";
125-
PushSubscriptionId = IsE2EMode ? MaskValue(rawPushId) : rawPushId;
130+
var rawPushId = OneSignal.User.PushSubscription.Id;
131+
PushSubscriptionId = MaskPushId(rawPushId, HasNotificationPermission);
126132
IsPushEnabled = OneSignal.User.PushSubscription.OptedIn;
127133

128134
var onesignalId = OneSignal.User.OneSignalId;
@@ -146,7 +152,7 @@ private void UpdateUserStatus(string? externalId)
146152
var loggedIn = !string.IsNullOrEmpty(externalId);
147153
LoginButtonText = loggedIn ? "SWITCH USER" : "LOGIN USER";
148154
UserStatus = loggedIn ? "Logged In" : "Anonymous";
149-
ExternalIdDisplay = loggedIn ? externalId! : "";
155+
ExternalIdDisplay = loggedIn ? externalId! : "";
150156
IsLoggedIn = loggedIn;
151157
}
152158

@@ -598,10 +604,13 @@ private void OnPushSubscriptionChanged(object? sender, PushSubscriptionChangedEv
598604
{
599605
MainThread.BeginInvokeOnMainThread(() =>
600606
{
601-
var rawPushId = OneSignal.User.PushSubscription.Id ?? "";
602-
PushSubscriptionId = IsE2EMode ? MaskValue(rawPushId) : rawPushId;
603-
IsPushEnabled = OneSignal.User.PushSubscription.OptedIn;
604-
Debug.WriteLine($"Push subscription changed: id={rawPushId}, optedIn={IsPushEnabled}");
607+
var previous = args.State.Previous;
608+
var current = args.State.Current;
609+
PushSubscriptionId = MaskPushId(current.Id, HasNotificationPermission);
610+
IsPushEnabled = current.OptedIn;
611+
Debug.WriteLine(
612+
$"Push subscription changed: id={previous.Id} -> {current.Id}, optedIn={previous.OptedIn} -> {current.OptedIn}, token={FormatToken(previous.Token)} -> {FormatToken(current.Token)}"
613+
);
605614
});
606615
}
607616

@@ -613,6 +622,7 @@ OneSignalSDK.DotNet.Core.Notifications.NotificationPermissionChangedEventArgs ar
613622
MainThread.BeginInvokeOnMainThread(() =>
614623
{
615624
HasNotificationPermission = args.Permission;
625+
PushSubscriptionId = MaskPushId(OneSignal.User.PushSubscription.Id, HasNotificationPermission);
616626
Debug.WriteLine($"Permission changed: {args.Permission}");
617627
});
618628
}

0 commit comments

Comments
 (0)