import axios from 'axios'; const url = 'https://screen.omborigrid.com/is-online.json'; const getUrl = () => `${url}?r=${Math.random()}`; interface OnlineSubscriptionOptions { checkIntervalMs: number; checkIntervalMsWhenOfflineMs: number; } const defaultOptions = { checkIntervalMs: 30 * 1000, checkIntervalMsWhenOfflineMs: 10 * 1000, }; type OnlineStatusSubscriptionListener = (props: GetOnlineStatusRequestResponse) => void; type GetOnlineStatusRequestResponse = { isOnline: boolean; message?: string; }; export const getOnlineStatusRequest = (timeoutMs: number) => { return new Promise((resolve, _reject) => { const requestTimeout = setTimeout( () => resolve({ isOnline: false, message: 'manual promise timeout', }), timeoutMs ); axios .get(getUrl()) .then(response => { resolve({ isOnline: response.data.isOnline, message: 'ping success', }); }) .catch(err => resolve({ isOnline: false, message: `ping error, ${err.message}`, }) ) .finally(() => clearTimeout(requestTimeout)); }); }; class OnlineStatusSubscription { private onlineCheckTimeout: NodeJS.Timeout | undefined; private readonly options: OnlineSubscriptionOptions; private activatedAt: number | null = null; private listeners: OnlineStatusSubscriptionListener[] = []; public isOnline: boolean | null = null; constructor(options?: Partial | undefined) { this.options = { ...defaultOptions, ...options }; } private getPollingIntervalDuration = () => { return this.isOnline ? this.options.checkIntervalMs : this.options.checkIntervalMsWhenOfflineMs; }; private getOnlineStatus = () => { const timeout = this.getPollingIntervalDuration() - 1; return getOnlineStatusRequest(timeout); }; private pollOnlineStatus = () => { const startActivatedAt = this.activatedAt; this.getOnlineStatus() .then(({ isOnline, message }) => { if (this.activatedAt && startActivatedAt === this.activatedAt) { if (this.isOnline !== isOnline) { this.isOnline = isOnline; this.emit({ isOnline, message }); } } }) .catch(err => { // this is not expected, there should be no error from the function console.error(`Error getting online status: ${err}`); }) .finally(() => { if (this.activatedAt && startActivatedAt === this.activatedAt) { this.onlineCheckTimeout = setTimeout( this.pollOnlineStatus, this.getPollingIntervalDuration() ); } }); }; public start = () => { if (!this.activatedAt) { this.activatedAt = Date.now(); this.pollOnlineStatus(); } }; public stop = () => { if (this.onlineCheckTimeout) { clearTimeout(this.onlineCheckTimeout); } this.activatedAt = null; }; private emit = (props: GetOnlineStatusRequestResponse) => { for (const listener of this.listeners) { listener(props); } }; public subscribe = (listener: OnlineStatusSubscriptionListener) => { this.listeners = this.listeners.concat([listener]); return () => this.unsubscribe(listener); }; public unsubscribe = (listener: OnlineStatusSubscriptionListener) => { this.listeners = this.listeners.filter(activeListener => activeListener !== listener); }; } export default OnlineStatusSubscription;