Skip to content
Open
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
6 changes: 4 additions & 2 deletions __test__/support/environment/TestEnvironmentHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ export const setupSubModelStore = async ({
token,
onesignalId,
});
await Database.setPushId(pushModel.id);
await Database.setPushToken(pushModel.token);
await Database.setTokenAndId({
token: pushModel.token,
id: pushModel.id,
});
OneSignal.coreDirector.subscriptionModelStore.replaceAll(
[pushModel],
ModelChangeTags.NO_PROPOGATE,
Expand Down
1 change: 0 additions & 1 deletion __test__/unit/models/deliveryPlatformKind.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { DeliveryPlatformKind } from '../../../src/shared/models/DeliveryPlatfor
describe('DeliveryPlatformKind', () => {
test('delivery platform constants should be correct', async () => {
expect(DeliveryPlatformKind.ChromeLike).toBe(5);
expect(DeliveryPlatformKind.SafariLegacy).toBe(7);
expect(DeliveryPlatformKind.Firefox).toBe(8);
expect(DeliveryPlatformKind.Email).toBe(11);
expect(DeliveryPlatformKind.Edge).toBe(12);
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,17 @@
"size-limit": [
{
"path": "./build/releases/OneSignalSDK.page.js",
"limit": "1 kB",
"limit": "640 B",
"gzip": true
},
{
"path": "./build/releases/OneSignalSDK.page.es6.js",
"limit": "65 kB",
"limit": "64.3 kB",
"gzip": true
},
{
"path": "./build/releases/OneSignalSDK.sw.js",
"limit": "37.5 kB",
"limit": "37 kB",
"gzip": true
},
{
Expand Down
6 changes: 4 additions & 2 deletions src/core/executors/LoginUserOperationExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ export class LoginUserOperationExecutor implements IOperationExecutor {

const pushSubscriptionId = await Database.getPushId();
if (pushSubscriptionId === localId) {
await Database.setPushId(backendSub.id);
await Database.setPushToken(backendSub.token);
await Database.setTokenAndId({
token: backendSub.token,
id: backendSub.id,
});
}

const model =
Expand Down
6 changes: 4 additions & 2 deletions src/core/executors/SubscriptionOperationExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,10 @@ export class SubscriptionOperationExecutor implements IOperationExecutor {

const pushSubscriptionId = await Database.getPushId();
if (pushSubscriptionId === createOperation.subscriptionId) {
await Database.setPushId(backendSubscriptionId);
await Database.setPushToken(subscription?.token);
await Database.setTokenAndId({
token: subscription?.token,
id: backendSubscriptionId,
});
}

return new ExecutionResponse(
Expand Down
24 changes: 1 addition & 23 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
import { OneSignalDeferredLoadedCallback } from './page/models/OneSignalDeferredLoadedCallback';

/**
* Types and names collected from:
* - https://developer.apple.com/documentation/safariextensions/safariremotenotification
* - https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/NotificationProgrammingGuideForWebsites/PushNotifications/PushNotifications.html
*/
interface SafariRemoteNotificationPermission {
readonly deviceToken: string | null;
readonly permission: 'default' | 'granted' | 'denied';
}

interface SafariRemoteNotification {
permission(
websitePushID: string | undefined,
): SafariRemoteNotificationPermission;
requestPermission(
webAPIURL: string,
websitePushID: string | undefined,
queryParameterDictionary: unknown,
callback: (permissionData: SafariRemoteNotificationPermission) => void,
): void;
}

type _OneSignal = typeof import('./onesignal/OneSignal').default;

declare global {
Expand All @@ -32,7 +10,7 @@ declare global {
OneSignalDeferred?: OneSignalDeferredLoadedCallback[];
__oneSignalSdkLoadCount?: number;
safari?: {
pushNotification?: SafariRemoteNotification;
pushNotification?: {};
};
}
}
5 changes: 3 additions & 2 deletions src/page/bell/Dialog.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import OneSignalEvent from '../../shared/services/OneSignalEvent';
import SdkEnvironment from '../../shared/managers/SdkEnvironment';
import OneSignalEvent from '../../shared/services/OneSignalEvent';
import { bowserCastle } from '../../shared/utils/bowserCastle';
import {
addDomElement,
clearDomElementChildren,
getPlatformNotificationIcon,
} from '../../shared/utils/utils';
import type { NotificationIcons } from '../types';
import AnimatedElement from './AnimatedElement';
import Bell from './Bell';
import { bowserCastle } from '../../shared/utils/bowserCastle';

export default class Dialog extends AnimatedElement {
public bell: Bell;
Expand Down
39 changes: 20 additions & 19 deletions src/page/slidedown/Slidedown.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import OneSignalEvent from '../../shared/services/OneSignalEvent';
import { SERVER_CONFIG_DEFAULTS_SLIDEDOWN } from '../../shared/config/constants';
import { Utils } from '../../shared/context/Utils';
import MainHelper from '../../shared/helpers/MainHelper';
import PromptsHelper from '../../shared/helpers/PromptsHelper';
import {
DelayedPromptType,
SlidedownPromptOptions,
} from '../../shared/models/Prompts';
import OneSignalEvent from '../../shared/services/OneSignalEvent';
import {
COLORS,
SLIDEDOWN_CSS_CLASSES,
SLIDEDOWN_CSS_IDS,
} from '../../shared/slidedown/constants';
import { bowserCastle } from '../../shared/utils/bowserCastle';
import {
addCssClass,
addDomElement,
getDomElementOrStub,
getPlatformNotificationIcon,
once,
removeDomElement,
removeCssClass,
getDomElementOrStub,
removeDomElement,
} from '../../shared/utils/utils';
import { SERVER_CONFIG_DEFAULTS_SLIDEDOWN } from '../../shared/config/constants';
import { InvalidChannelInputField } from '../errors/ChannelCaptureError';
import { TagCategory } from '../models/Tags';
import type { NotificationIcons } from '../types';
import ChannelCaptureContainer from './ChannelCaptureContainer';
import { getLoadingIndicatorWithColor } from './LoadingIndicator';
import { getRetryIndicator } from './RetryIndicator';
import {
SLIDEDOWN_CSS_CLASSES,
SLIDEDOWN_CSS_IDS,
COLORS,
} from '../../shared/slidedown/constants';
import { TagCategory } from '../models/Tags';
import { getSlidedownElement } from './SlidedownElement';
import { Utils } from '../../shared/context/Utils';
import ChannelCaptureContainer from './ChannelCaptureContainer';
import PromptsHelper from '../../shared/helpers/PromptsHelper';
import {
SlidedownPromptOptions,
DelayedPromptType,
} from '../../shared/models/Prompts';
import { InvalidChannelInputField } from '../errors/ChannelCaptureError';
import { bowserCastle } from '../../shared/utils/bowserCastle';

export default class Slidedown {
public options: SlidedownPromptOptions;
Expand Down
2 changes: 1 addition & 1 deletion src/page/models/NotificationIcons.ts → src/page/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
interface NotificationIcons {
export interface NotificationIcons {
chrome?: string;
firefox?: string;
safari?: string;
Expand Down
10 changes: 1 addition & 9 deletions src/page/userModel/FuturePushSubscriptionRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ export default class FuturePushSubscriptionRecord implements Serializable {
}

private _getToken(subscription: RawPushSubscription): string | undefined {
if (subscription.w3cEndpoint) {
return subscription.w3cEndpoint.toString();
}
return subscription.safariDeviceToken;
return subscription.w3cEndpoint?.toString();
}

serialize() {
Expand Down Expand Up @@ -70,9 +67,6 @@ export default class FuturePushSubscriptionRecord implements Serializable {
if (Environment.useSafariVapidPush()) {
return SubscriptionType.SafariPush;
}
if (Environment.useSafariLegacyPush()) {
return SubscriptionType.SafariLegacyPush;
}
// Other browsers, like Edge, are Chromium based so we consider them "Chrome".
return SubscriptionType.ChromePush;
}
Expand All @@ -85,8 +79,6 @@ export default class FuturePushSubscriptionRecord implements Serializable {
switch (this.getSubscriptionType()) {
case SubscriptionType.FirefoxPush:
return DeliveryPlatformKind.Firefox;
case SubscriptionType.SafariLegacyPush:
return DeliveryPlatformKind.SafariLegacy;
case SubscriptionType.SafariPush:
return DeliveryPlatformKind.SafariVapid;
}
Expand Down
3 changes: 2 additions & 1 deletion src/page/utils/BrowserSupportsPush.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export function isPushNotificationsSupported() {
return supportsVapidPush() || supportsSafariLegacyPush();
}

// Does the browser support legacy Safari push? (only available on macOS)
// Allow app to run with legacy safari push notifications. If safari version is newer then
// the subscription will ported in SubscriptionManager _updatePushSubscriptionModelWithRawSubscription
export function supportsSafariLegacyPush(): boolean {
return (
typeof window.safari !== 'undefined' &&
Expand Down
14 changes: 1 addition & 13 deletions src/shared/helpers/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,8 @@ export default class Environment {
return typeof window !== 'undefined';
}

// Prefer Legacy Safari if API is available over VAPID until Safari
// fixes issues with it.
public static useSafariLegacyPush(): boolean {
return this.isBrowser() && window.safari?.pushNotification != undefined;
}

// This is the counter part to useSafariLegacyPush(); as it notes only use
// Safari VAPID if it doesn't have legacy Safari push.
public static useSafariVapidPush(): boolean {
return (
bowserCastle().name == 'safari' &&
supportsVapidPush() &&
!this.useSafariLegacyPush()
);
return bowserCastle().name == 'safari' && supportsVapidPush();
}

public static version() {
Expand Down
8 changes: 0 additions & 8 deletions src/shared/helpers/MainHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
import Database from '../services/Database';
import { PermissionUtils } from '../utils/PermissionUtils';
import { getPlatformNotificationIcon, logMethodCall } from '../utils/utils';
import Environment from './Environment';

export default class MainHelper {
static async showLocalNotification(
Expand Down Expand Up @@ -234,13 +233,6 @@ export default class MainHelper {

// TO DO: unit test
static async getCurrentPushToken(): Promise<string | undefined> {
if (Environment.useSafariLegacyPush()) {
const safariToken = window.safari?.pushNotification?.permission(
OneSignal.config.safariWebId,
).deviceToken;
return safariToken?.toLowerCase() || undefined;
}

const registration =
await OneSignal.context.serviceWorkerManager.getRegistration();
if (!registration) {
Expand Down
43 changes: 3 additions & 40 deletions src/shared/managers/PermissionManager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import {
InvalidArgumentError,
InvalidArgumentReason,
} from '../errors/InvalidArgumentError';
import { NotificationPermission } from '../models/NotificationPermission';
import OneSignalError from '../errors/OneSignalError';
import Environment from '../helpers/Environment';
import { NotificationPermission } from '../models/NotificationPermission';

/**
* A permission manager to consolidate the different quirks of obtaining and evaluating permissions
Expand All @@ -26,47 +21,15 @@ export default class PermissionManager {
);
}

return await OneSignal.context.permissionManager.getNotificationPermission(
OneSignal.config!.safariWebId,
);
return await OneSignal.context.permissionManager.getNotificationPermission();
}

/**
* Notification permission reported by the browser.
*
* @param safariWebId The Safari web ID necessary to access the permission
* state on Legacy Safari on macOS.
*/
public async getNotificationPermission(
safariWebId?: string,
): Promise<NotificationPermission> {
if (Environment.useSafariLegacyPush()) {
return PermissionManager.getLegacySafariNotificationPermission(
safariWebId,
);
}
return this.getW3cNotificationPermission();
}

/**
* Returns the Safari browser's notification permission as reported by the browser.
*
* @param safariWebId The Safari web ID necessary for Legacy Safari on macOS.
*/
private static getLegacySafariNotificationPermission(
safariWebId?: string,
): NotificationPermission {
if (safariWebId)
return window.safari.pushNotification.permission(safariWebId)
.permission as NotificationPermission;
throw new InvalidArgumentError('safariWebId', InvalidArgumentReason.Empty);
Comment on lines -60 to -61
Copy link
Member

@jkasten2 jkasten2 Jul 10, 2025

Choose a reason for hiding this comment

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

We need to check if window.safari.pushNotification.permission(safariWebId) returns false, and only then use the new VAPID push. This is due to a bug that still exists today in Safari that doesn't allow migrating someone who has accepted Safari legacy push for the domain to standard notifications.

See this Asana ticket for more details:
https://app.asana.com/1/780103692902078/project/1204295426001934/task/1204303093244836?focus=true

}

/**
* Returns the notification permission as reported by the browser.
* - Expect for legacy Safari on macOS.
*/
private getW3cNotificationPermission(): NotificationPermission {
public getNotificationPermission(): NotificationPermission {
return Notification.permission as NotificationPermission;
}
}
4 changes: 1 addition & 3 deletions src/shared/managers/ServiceWorkerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,7 @@ export class ServiceWorkerManager {
workerState === ServiceWorkerActiveState.ThirdParty
) {
const permission =
await OneSignal.context.permissionManager.getNotificationPermission(
OneSignal.config!.safariWebId,
);
await OneSignal.context.permissionManager.getNotificationPermission();
const notificationsEnabled = permission === 'granted';
if (notificationsEnabled) {
Log.info(
Expand Down
Loading
Loading