onesignal-web-sdk
Version:
Web push notifications from OneSignal.
204 lines (171 loc) • 7.83 kB
text/typescript
import { hasCssClass, addCssClass, removeCssClass } from "./utils";
import { AppUserConfigCustomLinkOptions } from "./models/AppConfig";
import { ResourceLoadState } from "./services/DynamicResourceLoader";
import Log from "./libraries/Log";
import { RegisterOptions } from './helpers/InitHelper';
export class CustomLink {
public static readonly initializedAttribute = "data-cl-initialized";
public static readonly subscriptionStateAttribute = "data-cl-state";
public static readonly optedOutAttribute = "data-cl-optedout";
public static readonly containerClass = "onesignal-customlink-container";
public static readonly containerSelector = `.${CustomLink.containerClass}`;
public static readonly subscribeClass = "onesignal-customlink-subscribe";
public static readonly subscribeSelector = `.${CustomLink.subscribeClass}`;
public static readonly explanationClass = "onesignal-customlink-explanation";
public static readonly explanationSelector = `.${CustomLink.explanationClass}`;
public static readonly resetClass = "onesignal-reset";
public static async initialize(config: AppUserConfigCustomLinkOptions | undefined): Promise<void> {
if (!config || !config.enabled) {
return;
}
Log.info("Inititalize CustomLink");
const sdkStylesLoadResult = await OneSignal.context.dynamicResourceLoader.loadSdkStylesheet();
if (sdkStylesLoadResult !== ResourceLoadState.Loaded) {
Log.debug('Not initializing custom link button because styles failed to load.');
return;
}
const containerElements = document.querySelectorAll<HTMLElement>(CustomLink.containerSelector);
containerElements.forEach((element: HTMLElement) => {
if (!CustomLink.isInitialized(element)) {
CustomLink.injectMarkup(element, config);
}
});
const isPushEnabled = await OneSignal.privateIsPushNotificationsEnabled();
const isOptedOut = await OneSignal.internalIsOptedOut();
const subscribeElements = document.querySelectorAll<HTMLElement>(CustomLink.subscribeSelector);
subscribeElements.forEach((element: HTMLElement) =>
CustomLink.initSubscribeElement(element, config, isPushEnabled, isOptedOut));
const explanationElements = document.querySelectorAll<HTMLElement>(CustomLink.explanationSelector);
explanationElements.forEach((element: HTMLElement) =>
CustomLink.initExplanationElement(element, config, isPushEnabled));
}
private static injectMarkup(container: HTMLElement,
config: AppUserConfigCustomLinkOptions,): void {
if (!config.text) {
Log.error("CustomLink: required property 'text' is missing in the config");
return;
}
// Clearing out the contents of the container first
container.innerHTML = '';
if (config.text.explanation) {
const explanation = document.createElement("p");
addCssClass(explanation, CustomLink.explanationClass);
container.appendChild(explanation);
}
if (config.text.subscribe) {
const subscribe = document.createElement("button");
addCssClass(subscribe, CustomLink.subscribeClass);
container.appendChild(subscribe);
}
CustomLink.markAsInitialized(container);
}
private static initSubscribeElement(element: HTMLElement,
config: AppUserConfigCustomLinkOptions, isPushEnabled: boolean, isOptedOut: boolean): void {
if (config.text && config.text.subscribe) {
if (!isPushEnabled) {
element.textContent = config.text.subscribe;
}
}
if (config.text && config.text.unsubscribe) {
if (isPushEnabled) {
element.textContent = config.text.unsubscribe;
}
}
CustomLink.setResetClass(element);
CustomLink.setStateClass(element, isPushEnabled);
CustomLink.setStyleClass(element, config);
CustomLink.setSizeClass(element, config);
CustomLink.setCustomColors(element, config);
if (config.unsubscribeEnabled !== true) {
addCssClass(element, "hide");
}
element.setAttribute(CustomLink.subscriptionStateAttribute, isPushEnabled.toString());
element.setAttribute(CustomLink.optedOutAttribute, isOptedOut.toString());
if (!CustomLink.isInitialized(element)) {
element.addEventListener("click", () => {
Log.info("CustomLink: subscribe clicked");
CustomLink.handleClick(element);
});
CustomLink.markAsInitialized(element);
}
}
public static async handleClick(element: HTMLElement): Promise<void> {
const state: boolean = element.getAttribute(CustomLink.subscriptionStateAttribute) === "true";
const optedOut: boolean = element.getAttribute(CustomLink.optedOutAttribute) === "true";
if (state) {
const isPushEnabled = await OneSignal.privateIsPushNotificationsEnabled();
if (isPushEnabled) {
await OneSignal.setSubscription(false);
}
} else {
if (!optedOut) {
const options: RegisterOptions = { autoAccept: true };
await OneSignal.registerForPushNotifications(options);
} else {
await OneSignal.setSubscription(true);
}
}
}
private static initExplanationElement(element: HTMLElement,
config: AppUserConfigCustomLinkOptions, isPushEnabled: boolean): void {
if (config.text && config.text.explanation) {
element.textContent = config.text.explanation;
}
CustomLink.setResetClass(element);
CustomLink.setStateClass(element, isPushEnabled);
CustomLink.setSizeClass(element, config);
if (config.unsubscribeEnabled !== true) {
addCssClass(element, "hide");
}
}
// Using stricter HTMLElement class for element parameter to access style property
private static setCustomColors(element: HTMLElement, config: AppUserConfigCustomLinkOptions): void {
if (config.style === "button" && config.color && config.color.button && config.color.text) {
element.style.backgroundColor = config.color.button;
element.style.color = config.color.text;
} else if (config.style === "link" && config.color && config.color.text) {
element.style.color = config.color.text;
}
}
private static setStateClass(element: Element, subscribed: boolean): void {
const oldClassName = subscribed ? "state-unsubscribed" : "state-subscribed";
const newClassName = subscribed ? "state-subscribed" : "state-unsubscribed";
if (hasCssClass(element, oldClassName)) {
removeCssClass(element, oldClassName);
}
if (!hasCssClass(element, newClassName)) {
addCssClass(element, newClassName);
}
}
private static setStyleClass(element: Element, config: AppUserConfigCustomLinkOptions): void {
if (!config || !config.style) {
return
}
const newClassName = config.style;
if (!hasCssClass(element, newClassName)) {
addCssClass(element, newClassName);
}
}
private static setSizeClass(element: Element, config: AppUserConfigCustomLinkOptions): void {
if (!config || !config.size) {
return;
}
const newClassName = config.size;
if (!hasCssClass(element, newClassName)) {
addCssClass(element, newClassName);
}
}
private static setResetClass(element: Element): void {
const newClassName = CustomLink.resetClass;
if (!hasCssClass(element, newClassName)) {
addCssClass(element, newClassName);
}
}
private static markAsInitialized(element: Element): void {
element.setAttribute(CustomLink.initializedAttribute, "true");
}
private static isInitialized(element: Element): boolean {
return element.getAttribute(CustomLink.initializedAttribute) === "true";
}
}
export default CustomLink;