diff --git a/.eslintrc.js b/.eslintrc.js index b3bf2d8..9f4ca73 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,6 +37,7 @@ module.exports = { }, plugins: ['react', '@typescript-eslint', 'typescript-sort-keys', 'sort-keys-fix'], rules: { + 'no-await-in-loop': 'off', 'prettier/prettier': 'error', 'sort-keys-fix/sort-keys-fix': 'error', 'testing-library/no-debug': 'off', diff --git a/src/background.ts b/src/background.ts index b2af2aa..6d43701 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,11 +1,32 @@ import browser from 'webextension-polyfill'; import WakaTimeCore from './core/WakaTimeCore'; +// Add a listener to resolve alarms +browser.alarms.onAlarm.addListener(async (alarm) => { + // |alarm| can be undefined because onAlarm also gets called from + // window.setTimeout on old chrome versions. + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (alarm && alarm.name == 'heartbeatAlarm') { + // Checks if the user is online and if there are cached heartbeats requests, + // if so then procedd to send these payload to wakatime api + if (navigator.onLine) { + const { cachedHeartbeats } = await browser.storage.sync.get({ + cachedHeartbeats: [], + }); + await browser.storage.sync.set({ cachedHeartbeats: [] }); + await WakaTimeCore.sendCachedHeartbeatsRequest(cachedHeartbeats as Record[]); + } + } +}); + +// Create a new alarm for sending cached heartbeats. +browser.alarms.create('heartbeatAlarm', { periodInMinutes: 2 }); + /** * Whenever a active tab is changed it records a heartbeat with that tab url. */ browser.tabs.onActivated.addListener(async () => { - console.log('recording a heartbeat - active tab changed'); + console.log('recording a heartbeat - active tab changed '); await WakaTimeCore.recordHeartbeat(); }); diff --git a/src/core/WakaTimeCore.ts b/src/core/WakaTimeCore.ts index 1747436..3a828a6 100644 --- a/src/core/WakaTimeCore.ts +++ b/src/core/WakaTimeCore.ts @@ -262,9 +262,44 @@ class WakaTimeCore { const data = await response.json(); return data; } catch (err: unknown) { + // Stores the payload of the request to be send later + const { cachedHeartbeats } = await browser.storage.sync.get({ + cachedHeartbeats: [], + }); + cachedHeartbeats.push(payload); + await browser.storage.sync.set({ cachedHeartbeats }); await changeExtensionState('notSignedIn'); } } + + /** + * Sends cached heartbeats request to wakatime api + * @param requests + */ + async sendCachedHeartbeatsRequest(requests: Record[]): Promise { + const apiKey = await this.getApiKey(); + if (!apiKey) { + return changeExtensionState('notLogging'); + } + const chunkSize = 50; // Create batches of max 50 request + for (let i = 0; i < requests.length; i += chunkSize) { + const chunk = requests.slice(i, i + chunkSize); + const requestsPromises: Promise[] = []; + chunk.forEach((request) => + requestsPromises.push( + fetch(`${config.heartbeatApiUrl}?api_key=${apiKey}`, { + body: JSON.stringify(request), + method: 'POST', + }), + ), + ); + try { + await Promise.all(requestsPromises); + } catch (error: unknown) { + console.log('Error sending heartbeats'); + } + } + } } export default new WakaTimeCore();