onesignal-web-sdk
Version:
Web push notifications from OneSignal.
97 lines (85 loc) • 3.15 kB
text/typescript
import SdkEnvironment from '../managers/SdkEnvironment';
import Environment from '../Environment';
export const enum ResourceType {
Stylesheet,
Script
}
export const enum ResourceLoadState {
/**
* The remote resource was fetched and loaded successfully.
*/
Loaded,
/**
* The remote resource failed to be loaded (e.g. not found or network offline).
*/
Failed
}
interface DynamicResourceLoaderCache {
[key: string]: Promise<ResourceLoadState>;
}
export class DynamicResourceLoader {
private cache: DynamicResourceLoaderCache;
constructor() {
this.cache = {};
}
getCache(): DynamicResourceLoaderCache {
// Cache is private; return a cloned copy just for testing
return {...this.cache};
}
async loadSdkStylesheet(): Promise<ResourceLoadState> {
const pathForEnv = SdkEnvironment.getOneSignalResourceUrlPath();
const cssFileForEnv = SdkEnvironment.getOneSignalCssFileName();
return await this.loadIfNew(
ResourceType.Stylesheet,
new URL(`${pathForEnv}/${cssFileForEnv}?v=${Environment.getSdkStylesVersionHash()}`)
);
}
async loadFetchPolyfill(): Promise<ResourceLoadState> {
return await this.loadIfNew(
ResourceType.Script,
new URL('https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js')
);
}
/**
* Attempts to load a resource by adding it to the document's <head>.
* Caches any previous load attempt's result and does not retry loading a previous resource.
*/
async loadIfNew(type: ResourceType, url: URL): Promise<ResourceLoadState> {
// Load for first time
if (!this.cache[url.toString()]) {
this.cache[url.toString()] = DynamicResourceLoader.load(type, url);
}
// Resource is loading; multiple calls can be made to this while the same resource is loading
// Waiting on the Promise is what we want here
return await this.cache[url.toString()];
}
/**
* Attempts to load a resource by adding it to the document's <head>.
* Each call creates a new DOM element and fetch attempt.
*/
static async load(type: ResourceType, url: URL): Promise<ResourceLoadState> {
try {
await new Promise((resolve, reject) => {
switch (type) {
case ResourceType.Script:
var domElement: HTMLElement = document.createElement('script');
domElement.setAttribute('type', 'text/javascript');
domElement.setAttribute('async', 'async');
domElement.setAttribute('src', url.toString());
break;
case ResourceType.Stylesheet:
var domElement: HTMLElement = document.createElement('link');
domElement.setAttribute('rel', 'stylesheet');
domElement.setAttribute('href', url.toString());
break;
}
domElement.onerror = reject;
domElement.onload = resolve;
document.querySelector('head').appendChild(domElement);
});
return ResourceLoadState.Loaded;
} catch (e) {
return ResourceLoadState.Failed;
}
}
}