From 51fe7e53724d1df6ebd43bd6f524c561f80ed1be Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Tue, 27 Aug 2024 01:12:51 +0900 Subject: [PATCH 1/5] Sends install and update events to GA4 --- src/background.js | 6 ++-- src/google-analytics.js | 2 ++ src/installation-handler.js | 22 ++++++++++++++ test/installation-hander.test.js | 51 ++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 src/installation-handler.js create mode 100644 test/installation-hander.test.js diff --git a/src/background.js b/src/background.js index c66eac4..077b176 100644 --- a/src/background.js +++ b/src/background.js @@ -1,10 +1,10 @@ import { getClientId } from './id.js' -import { sendTrackEvent } from './google-analytics.js' +import { installationHandler } from './installation-handler.js' import contextMenuRepository from './context_menu' -chrome.runtime.onInstalled.addListener(function () { +chrome.runtime.onInstalled.addListener(function ({ previousVersion, reason }) { getClientId() - sendTrackEvent({ name: 'installed' }) + .then((_clientId) => installationHandler({ previousVersion, reason })) contextMenuRepository.getContextMenuInfo().forEach((contextMenuInfo) => { chrome.contextMenus.create(contextMenuInfo) diff --git a/src/google-analytics.js b/src/google-analytics.js index 0fd8eb4..afb9ee5 100644 --- a/src/google-analytics.js +++ b/src/google-analytics.js @@ -11,6 +11,8 @@ async function sendTrackEvent(event) { engagement_time_msec: 100 } + console.debug('Sending event:', event); + return fetch( `${GA_ENDPOINT}?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`, { diff --git a/src/installation-handler.js b/src/installation-handler.js new file mode 100644 index 0000000..d40389b --- /dev/null +++ b/src/installation-handler.js @@ -0,0 +1,22 @@ +import { sendTrackEvent } from "./google-analytics" + +export async function installationHandler({ previousVersion, reason }) { + const { version } = chrome.runtime.getManifest() + + if (reason === 'install') { + return sendTrackEvent({ name: 'installed', params: { version } }) + .then(() => chrome.storage.local.set({ lastInstalledVersion: version })) + .then(() => console.debug(`lastInstalledVersion is set: ${version}`)) + } else if (reason === 'update') { + chrome.storage.local.get('lastInstalledVersion') + .then(({ lastInstalledVersion }) => { + if (lastInstalledVersion) { + console.debug(`lastInstalledVersion is ${lastInstalledVersion}`) + return sendTrackEvent({ name: 'updated', params: { version, previousVersion } }) + } + + return sendTrackEvent({ name: 'installed', params: { version } }) + .then(() => chrome.storage.local.set({ lastInstalledVersion: version })) + }) + } +} \ No newline at end of file diff --git a/test/installation-hander.test.js b/test/installation-hander.test.js new file mode 100644 index 0000000..0ac4662 --- /dev/null +++ b/test/installation-hander.test.js @@ -0,0 +1,51 @@ +import { installationHandler } from "../src/installation-handler.js" +import { sendTrackEvent } from "../src/google-analytics.js" + +global.chrome = { + runtime: { + getManifest: () => ({ version: '4.4.4' }) + }, + storage: { + local: { + set: jest.fn(() => Promise.resolve()), + get: jest.fn(() => Promise.resolve({})), + }, + } +} + +jest.mock('../src/google-analytics.js', () => ({ sendTrackEvent: jest.fn() })) + +describe('installationHandler', () => { + beforeEach(() => { + chrome.storage.local.set.mockClear() + sendTrackEvent.mockClear() + + sendTrackEvent.mockReturnValue(Promise.resolve()) + }) + + describe('When the reason is install', () => { + it('should send installed event to google analytics', async () => { + await installationHandler({ reason: 'install' }) + + expect(sendTrackEvent).toHaveBeenCalledWith({ name: 'installed', params: { version: '4.4.4' } }) + expect(chrome.storage.local.set).toHaveBeenCalledWith({ lastInstalledVersion: '4.4.4' }) + }) + }) + + describe('When the reason is update', () => { + it('should send updated event to google analytics', async () => { + chrome.storage.local.get.mockReturnValue(Promise.resolve({ lastInstalledVersion: '3.3.3' })) + await installationHandler({ reason: 'update', previousVersion: '4.4.3' }) + + expect(sendTrackEvent).toHaveBeenCalledWith({ name: 'updated', params: { version: '4.4.4', previousVersion: '4.4.3' } }) + expect(chrome.storage.local.set).not.toHaveBeenCalled() + }) + + it('should send installed event if latestInstalledVersion is not set', async () => { + chrome.storage.local.get.mockReturnValue(Promise.resolve({ lastInstalledVersion: null })) + await installationHandler({ reason: 'update', previousVersion: '4.4.3' }) + + expect(sendTrackEvent).toHaveBeenCalledWith({ name: 'installed', params: { version: '4.4.4' } }) + }) + }) +}) From 314574c1f70e3643ffbcf5ca3636b29e845bdec4 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Mon, 16 Sep 2024 16:50:54 +0900 Subject: [PATCH 2/5] Refactoring --- src/installation-handler.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/installation-handler.js b/src/installation-handler.js index d40389b..4cf36e2 100644 --- a/src/installation-handler.js +++ b/src/installation-handler.js @@ -1,22 +1,34 @@ import { sendTrackEvent } from "./google-analytics" +async function saveLastInstalledVersion() { + const { version } = chrome.runtime.getManifest() + + return chrome.storage.local.set({ lastInstalledVersion: version }) +} + +async function getLastInstalledVersion() { + return chrome.storage.local.get('lastInstalledVersion') + .then(({ lastInstalledVersion }) => lastInstalledVersion) +} + export async function installationHandler({ previousVersion, reason }) { const { version } = chrome.runtime.getManifest() if (reason === 'install') { return sendTrackEvent({ name: 'installed', params: { version } }) - .then(() => chrome.storage.local.set({ lastInstalledVersion: version })) + .then(saveLastInstalledVersion) .then(() => console.debug(`lastInstalledVersion is set: ${version}`)) } else if (reason === 'update') { - chrome.storage.local.get('lastInstalledVersion') - .then(({ lastInstalledVersion }) => { + getLastInstalledVersion() + .then((lastInstalledVersion) => { + // if already installed, just update the version if (lastInstalledVersion) { console.debug(`lastInstalledVersion is ${lastInstalledVersion}`) return sendTrackEvent({ name: 'updated', params: { version, previousVersion } }) } return sendTrackEvent({ name: 'installed', params: { version } }) - .then(() => chrome.storage.local.set({ lastInstalledVersion: version })) + .then(saveLastInstalledVersion) }) } } \ No newline at end of file From e057d8b3c0cf543be9ec3d6bf9f0062f3e4504fb Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Mon, 16 Sep 2024 17:40:29 +0900 Subject: [PATCH 3/5] Add a contents to be opened when this extension is uninstalled. --- docs/thank-you.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/thank-you.md diff --git a/docs/thank-you.md b/docs/thank-you.md new file mode 100644 index 0000000..29daf40 --- /dev/null +++ b/docs/thank-you.md @@ -0,0 +1,14 @@ +--- +layout: page +title: Thank You for Using Copy for Scrapbox! +--- + +# Thank you for Using Copy for Scrapbox! + +We are sorry to see you go! Your feedback is important to us. +Please take a moment to let us know why you decided to uninstall the extension. + +- [GitHub Issues](https://github.com/satoryu/copy-for-scrapbox/issues) +- Email: satoryu.1981@gmail.com + +Thank you for your time and feedback! From 65bc20d712e676ae7d2c5c137079dd726bca7c54 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Mon, 16 Sep 2024 17:46:40 +0900 Subject: [PATCH 4/5] Open thank you page when this extension is uninstalled --- src/background.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/background.js b/src/background.js index 077b176..f2029e8 100644 --- a/src/background.js +++ b/src/background.js @@ -6,6 +6,8 @@ chrome.runtime.onInstalled.addListener(function ({ previousVersion, reason }) { getClientId() .then((_clientId) => installationHandler({ previousVersion, reason })) + chrome.runtime.setUninstallURL('https://www.satoryu.com/copy-for-scrapbox/thank-you') + contextMenuRepository.getContextMenuInfo().forEach((contextMenuInfo) => { chrome.contextMenus.create(contextMenuInfo) }) From a802c24fe82e8fb4b014349b0e0a48c1f1adc308 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Mon, 16 Sep 2024 18:28:04 +0900 Subject: [PATCH 5/5] Update async calls in installation handler --- src/installation-handler.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/installation-handler.js b/src/installation-handler.js index 4cf36e2..0e2c556 100644 --- a/src/installation-handler.js +++ b/src/installation-handler.js @@ -15,19 +15,20 @@ export async function installationHandler({ previousVersion, reason }) { const { version } = chrome.runtime.getManifest() if (reason === 'install') { - return sendTrackEvent({ name: 'installed', params: { version } }) + await sendTrackEvent({ name: 'installed', params: { version } }) .then(saveLastInstalledVersion) .then(() => console.debug(`lastInstalledVersion is set: ${version}`)) } else if (reason === 'update') { - getLastInstalledVersion() + await getLastInstalledVersion() .then((lastInstalledVersion) => { // if already installed, just update the version if (lastInstalledVersion) { console.debug(`lastInstalledVersion is ${lastInstalledVersion}`) - return sendTrackEvent({ name: 'updated', params: { version, previousVersion } }) + sendTrackEvent({ name: 'updated', params: { version, previousVersion } }) + return } - return sendTrackEvent({ name: 'installed', params: { version } }) + sendTrackEvent({ name: 'installed', params: { version } }) .then(saveLastInstalledVersion) }) }