using my boilerplate yuh

This commit is contained in:
Sriram Hariharan
2023-02-22 22:51:38 -06:00
parent 21d7056aae
commit bce2717088
91 changed files with 32400 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
import { MessageListener } from 'chrome-extension-toolkit';
import { BACKGROUND_MESSAGES } from 'src/shared/messages';
import { generateRandomId } from 'src/shared/util/random';
import onHistoryStateUpdated from './events/onHistoryStateUpdated';
import onInstall from './events/onInstall';
import onNewChromeSession from './events/onNewChromeSession';
import onServiceWorkerAlive from './events/onServiceWorkerAlive';
import onUpdate from './events/onUpdate';
import { sessionStore } from '../shared/storage/sessionStore';
import browserActionHandler from './handler/browserActionHandler';
import hotReloadingHandler from './handler/hotReloadingHandler';
import tabManagementHandler from './handler/tabManagementHandler';
onServiceWorkerAlive();
/**
* will be triggered on either install or update
* (will also be triggered on a user's sync'd browsers (on other devices)))
*/
chrome.runtime.onInstalled.addListener(details => {
switch (details.reason) {
case 'install':
onInstall();
break;
case 'update':
onUpdate();
break;
default:
break;
}
});
// This event is fired when any tab's url changes.
chrome.webNavigation.onHistoryStateUpdated.addListener(onHistoryStateUpdated);
// initialize the message listener that will listen for messages from the content script
const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
...browserActionHandler,
...hotReloadingHandler,
...tabManagementHandler,
});
messageListener.listen();
sessionStore.getChromeSessionId().then(async chromeSessionId => {
if (!chromeSessionId) {
await sessionStore.setChromeSessionId(generateRandomId(10));
onNewChromeSession();
}
});

View File

@@ -0,0 +1,11 @@
/**
* This event is fired when any tab's url changes.
* This is useful for content scripts to know when SPA navigations occur.
* @param details
*/
export default function onHistoryStateUpdated(
details: chrome.webNavigation.WebNavigationTransitionCallbackDetails
): void {
const { tabId, url } = details;
// TODO: send a message to tab with tabId to reanalyze the page
}

View File

@@ -0,0 +1,26 @@
import { SECOND } from 'src/shared/util/time';
/**
* Called when the extension is first installed or synced onto a new machine
*/
export default async function onInstall() {
// set the uninstall url
chrome.runtime.setUninstallURL('https://www.google.com');
logOnInstallEvent();
}
/**
* making sure we are not sending duplicate install event for users that have synced browsers
* sync storage get's cleared on browser uninstall, so re-installing the browser will trigger the event
*/
function logOnInstallEvent() {
setTimeout(async () => {
const manifest = chrome.runtime.getManifest();
const INSTALL_KEY = `${manifest.short_name}-installed`;
const storage = await chrome.storage.sync.get(INSTALL_KEY);
if (!storage[INSTALL_KEY]) {
// TODO: send install event
await chrome.storage.sync.set({ [INSTALL_KEY]: true });
}
}, 5 * SECOND);
}

View File

@@ -0,0 +1,4 @@
/**
* This function is called when the user's browser opens for the first time
*/
export default function onNewChromeSession() {}

View File

@@ -0,0 +1,9 @@
import { openDebugTab } from '../util/openDebugTab';
/**
* Called whenever the background service worker comes alive
* (usually around 30 seconds to 5 minutes after it was last alive)
*/
export default function onServiceWorkerAlive() {
openDebugTab();
}

View File

@@ -0,0 +1,10 @@
import { hotReloadTab } from 'src/background/util/hotReloadTab';
/**
* Called when the extension is updated (or when the extension is reloaded in development mode)
*/
export default function onUpdate() {
if (process.env.NODE_ENV === 'development') {
hotReloadTab();
}
}

View File

@@ -0,0 +1,20 @@
import { MessageHandler } from 'chrome-extension-toolkit';
import BrowserActionMessages from 'src/shared/messages/BrowserActionMessages';
const browserActionHandler: MessageHandler<BrowserActionMessages> = {
disableBrowserAction({ sender, sendResponse }) {
// by setting the popup to an empty string, clicking the browser action will not open the popup.html.
// we can then add an onClickListener to it from the content script
chrome.action.setPopup({ tabId: sender.tab?.id, popup: '' }).then(sendResponse);
},
enableBrowserAction({ sender, sendResponse }) {
chrome.action
.setPopup({
tabId: sender.tab?.id,
popup: 'popup.html',
})
.then(sendResponse);
},
};
export default browserActionHandler;

View File

@@ -0,0 +1,21 @@
import HotReloadingMessages from 'src/shared/messages/HotReloadingMessages';
import { MessageHandler } from 'chrome-extension-toolkit';
import { devStore } from 'src/shared/storage/devStore';
const hotReloadingHandler: MessageHandler<HotReloadingMessages> = {
async reloadExtension({ sendResponse }) {
const isExtensionReloading = await devStore.getIsExtensionReloading();
if (!isExtensionReloading) return sendResponse();
const isTabReloading = await devStore.getIsExtensionReloading();
if (isTabReloading) {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
const tabToReload = tabs[0];
await devStore.setReloadTabId(tabToReload?.id);
}
chrome.runtime.reload();
},
};
export default hotReloadingHandler;

View File

@@ -0,0 +1,18 @@
import { MessageHandler } from 'chrome-extension-toolkit';
import TabManagementMessages from 'src/shared/messages/TabManagementMessages';
const tabManagementHandler: MessageHandler<TabManagementMessages> = {
getTabId({ sendResponse, sender }) {
sendResponse(sender.tab?.id ?? -1);
},
openNewTab({ data, sendResponse }) {
const { url } = data;
chrome.tabs.create({ url }).then(sendResponse);
},
removeTab({ data, sendResponse }) {
const { tabId } = data;
chrome.tabs.remove(tabId).then(sendResponse);
},
};
export default tabManagementHandler;

View File

@@ -0,0 +1,39 @@
import { devStore } from 'src/shared/storage/devStore';
/**
* A list of websites that we don't want to reload when the extension reloads (becuase it'd be hella annoying lmao)
*/
const HOT_RELOADING_WHITELIST = [
'youtube.com',
'twitch.tv',
'github.dev',
'figma.com',
'netflix.com',
'disneyplus.com',
'hbomax.com',
'spotify.com',
'localhost:6006',
'docs.google.com',
'reddit.com',
'gmail.com',
'photopea.com',
];
/**
* Reloads the tab that was open when the extension was reloaded
* @returns a promise that resolves when the tab is reloaded
*/
export async function hotReloadTab(): Promise<void> {
const { getIsTabReloading, getReloadTabId } = devStore;
const [isTabReloading, reloadTabId] = await Promise.all([getIsTabReloading(), getReloadTabId()]);
if (!isTabReloading || !reloadTabId) return;
chrome.tabs.get(reloadTabId, tab => {
if (!tab?.id) return;
if (!HOT_RELOADING_WHITELIST.find(url => tab.url?.includes(url))) {
chrome.tabs.reload(tab.id);
}
});
}

View File

@@ -0,0 +1,24 @@
import { devStore } from 'src/shared/storage/devStore';
/**
* Open the debug tab as the first tab
*/
export async function openDebugTab() {
if (process.env.NODE_ENV === 'development') {
const debugTabId = await devStore.getDebugTabId();
const isAlreadyOpen = await (await chrome.tabs.query({})).some(tab => tab.id === debugTabId);
if (isAlreadyOpen) return;
const wasVisible = await devStore.getWasDebugTabVisible();
const tab = await chrome.tabs.create({
url: chrome.runtime.getURL('debug.html'),
active: wasVisible,
pinned: true,
index: 0,
});
await devStore.setDebugTabId(tab.id);
}
}