Skip to content

Commit

Permalink
feat: settings action menu (#52)
Browse files Browse the repository at this point in the history
First work of #27.

In this PR I created the infrastructure for controlling the state of the
extension from its popup.

See the review comments for specific implementation details.

![tinywow_Recording 2023-04-03
200726_18612307](https://user-images.githubusercontent.com/17686879/229579780-e3326381-9db4-440c-bbed-9a5036070a1b.gif)

---------

Co-authored-by: Jossef Harush Kadouri <[email protected]>
  • Loading branch information
baruchiro and jossef authored Apr 20, 2023
1 parent 2adf4a2 commit 5a77ae4
Show file tree
Hide file tree
Showing 19 changed files with 224 additions and 29 deletions.
32 changes: 24 additions & 8 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ const FILE_EXTENSION_XPI = 'xpi';
const BROWSER_TYPE_CHROME = 'chrome';
const BROWSER_TYPE_FIREFOX = 'firefox';

let scriptFilePath = url.fileURLToPath(import.meta.url);
let scriptDirPath = path.dirname(scriptFilePath);
let srcDirPath = path.resolve(scriptDirPath, 'src');
let distDirPath = path.resolve(scriptDirPath, 'dist');
const scriptFilePath = url.fileURLToPath(import.meta.url);
const scriptDirPath = path.dirname(scriptFilePath);
const srcDirPath = path.resolve(scriptDirPath, 'src');
const distDirPath = path.resolve(scriptDirPath, 'dist');
const popupDistDirPath = path.resolve(distDirPath, 'popup');

async function buildCustomElements(outputDirPath) {
await vite.build({
Expand All @@ -41,10 +42,7 @@ async function buildCustomElements(outputDirPath) {
},
},
define: {
'process.env': {
// TODO: Need to run this for each extension (chrome/firefox)
EXTENSION_ID: process.env.EXTENSION_ID,
},
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
plugins: [vue({ customElement: true }), svgLoader()],
});
Expand Down Expand Up @@ -76,6 +74,19 @@ async function buildCustomElements(outputDirPath) {
}
}

async function buildPopup(outputDirPath) {
const popupRoot = path.join(srcDirPath, 'popup');
await vite.build({
root: popupRoot,
base: '',
build: {
emptyOutDir: false,
outDir: outputDirPath,
},
plugins: [vue()],
});
}

async function buildBrowserExtension(browserType, version, fileExtension) {
let outputDirPath = path.join(distDirPath, browserType);

Expand All @@ -88,6 +99,10 @@ async function buildBrowserExtension(browserType, version, fileExtension) {
await gulp.src(path.join(distDirPath, 'custom-elements.js')).pipe(gulp.dest(path.join(outputDirPath)));
await gulp.src(path.join(distDirPath, 'custom-elements.css')).pipe(gulp.dest(path.join(outputDirPath)));

// --------------
// popup
await gulp.src(path.join(popupDistDirPath, '**', '*')).pipe(gulp.dest(path.join(outputDirPath, 'popup')));

// --------------
// manifest.json
let manifestFilename = `manifest.${browserType}.json`;
Expand Down Expand Up @@ -132,6 +147,7 @@ gulp.task('compile', async () => {
console.log(`compiling version ${version}`);

await buildCustomElements(distDirPath);
await buildPopup(popupDistDirPath);
await buildBrowserExtension(BROWSER_TYPE_CHROME, version, FILE_EXTENSION_ZIP);
await buildBrowserExtension(BROWSER_TYPE_FIREFOX, version, FILE_EXTENSION_XPI);
});
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@types/chrome": "^0.0.206",
"@types/firefox-webext-browser": "^111.0.1",
"@vitejs/plugin-vue": "^3.2.0",
"@webcomponents/custom-elements": "^1.5.1",
"del": "^7.0.0",
Expand Down
3 changes: 2 additions & 1 deletion src/background/background-events.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import browser from '../browser';
import { REQUEST_PACKAGE_INFO_EVENT, RESPONSE_PACKAGE_INFO_EVENT } from '../events-shared';
import advisories from './advisory/index';

Expand All @@ -22,7 +23,7 @@ const listener = async ({ type, detail }, port) => {
};

export const listen = () => {
chrome.runtime.onConnect.addListener((port) => {
browser.runtime.onConnect.addListener((port) => {
port.onMessage.addListener(listener);
});
};
1 change: 1 addition & 0 deletions src/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default globalThis.browser || globalThis.chrome;
16 changes: 15 additions & 1 deletion src/content/content-events.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import browser from '../browser';
import {
addMessagingEventListener,
CONTENT_PORT_CONNECTION,
dispatchEvent,
EVENT_SETTINGS_CHANGED,
READY_EVENT,
REQUEST_PACKAGE_INFO_EVENT,
RESPONSE_PACKAGE_INFO_EVENT,
} from '../events-shared';
import * as storage from '../storage';

const sendPackageInfoToWebapp = (info) => dispatchEvent(RESPONSE_PACKAGE_INFO_EVENT, info);

const backgroundConnection = chrome.runtime.connect({ name: CONTENT_PORT_CONNECTION });
const backgroundConnection = browser.runtime.connect({ name: CONTENT_PORT_CONNECTION });
backgroundConnection.onMessage.addListener((message) => {
if (message.type === RESPONSE_PACKAGE_INFO_EVENT) {
sendPackageInfoToWebapp(message.detail);
}
});

export const sendEventSettingsChangedToWebapp = async () => {
const settings = await storage.getAllAdvisoriesSettings();
dispatchEvent(EVENT_SETTINGS_CHANGED, settings);
};

export const fetchPackageInfo = (packageId) => {
backgroundConnection.postMessage({ type: REQUEST_PACKAGE_INFO_EVENT, detail: packageId });
};
Expand Down Expand Up @@ -43,4 +51,10 @@ export const listen = () => {
console.log('Ready event received from injected script');
isWebappReady = true;
});

browser.runtime.onMessage.addListener((message) => {
if (message.type === EVENT_SETTINGS_CHANGED) {
sendEventSettingsChangedToWebapp();
}
});
};
7 changes: 5 additions & 2 deletions src/content/content.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import browser from '../browser';
import * as events from './content-events';

const injectScriptTag = () => {
const script = document.createElement('script');
script.src = chrome.runtime.getURL('custom-elements.js');
script.src = browser.runtime.getURL('custom-elements.js');
(document.head || document.documentElement).appendChild(script);
console.log('Injected script tag', script);

const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = chrome.runtime.getURL('custom-elements.css');
link.href = browser.runtime.getURL('custom-elements.css');
(document.head || document.documentElement).appendChild(link);
console.log('Injected link tag', link);
};
Expand All @@ -27,6 +28,8 @@ export const mountContentScript = (contentScript) => {
return;
}

events.sendEventSettingsChangedToWebapp();

await contentScript();

console.log('Overlay is finished');
Expand Down
11 changes: 6 additions & 5 deletions src/custom-elements/Indicator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@

<script>
import { defineComponent } from 'vue';
import npm_logo from './assets/npm_logo.svg?component';
import store from './store';
import Tooltip from './Tooltip.vue';
import npm_logo from './assets/npm_logo.svg?component';
import { usePackageInfo } from './store';
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
Expand All @@ -42,6 +42,10 @@ export default defineComponent({
required: true,
},
},
setup(props) {
const packageInfo = usePackageInfo(props.overlayIndicatorPackageType, props.overlayIndicatorPackageName);
return { packageInfo };
},
data() {
return {
tooltipOpen: false,
Expand All @@ -50,9 +54,6 @@ export default defineComponent({
};
},
computed: {
packageInfo() {
return store.packages[this.overlayIndicatorPackageType]?.[this.overlayIndicatorPackageName];
},
issues() {
if (!this.packageInfo?.sources) return 0;
Expand Down
39 changes: 29 additions & 10 deletions src/custom-elements/store.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@
import { reactive } from 'vue';
import { computed, reactive } from 'vue';

const settings = reactive({});
const store = reactive({ packages: {} });
window.__overlay_global_store = store;

export const updatePackageInfo = ({ type, name }, part, info) => {
if (!store.packages[type]) {
store.packages[type] = {};
}
if (!store.packages[type][name]) {
store.packages[type][name] = {
const packageStoreID = ({ type, name }) => `${type}:${name}`;

export const updatePackageInfo = (packageID, part, data) => {
const packagePointer = packageStoreID(packageID);

if (!store.packages[packagePointer]) {
store.packages[packagePointer] = {
sources: {},
};
}

if (part === 'info') {
store.packages[type][name] = { ...store.packages[type][name], ...info };
store.packages[packagePointer] = { ...store.packages[packagePointer], ...data };
return;
}
store.packages[type][name].sources[part] = info;
store.packages[packagePointer].sources[part] = data;
};

export default store;
export const updateSettings = (newSettings) => {
Object.assign(settings, newSettings);
};

export const usePackageInfo = (type, name) =>
computed(() => {
const packageInfo = store.packages[packageStoreID({ type, name })];
if (!packageInfo) return null;

const filteredSources = Object.entries(packageInfo.sources).reduce((acc, [key, value]) => {
if (settings[key] !== false && value) {
acc[key] = value;
}
return acc;
}, {});

return { ...packageInfo, sources: filteredSources };
});
12 changes: 11 additions & 1 deletion src/custom-elements/webapp-events.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { addMessagingEventListener, dispatchEvent, READY_EVENT, RESPONSE_PACKAGE_INFO_EVENT } from '../events-shared.js';
import {
addMessagingEventListener,
dispatchEvent,
EVENT_SETTINGS_CHANGED,
READY_EVENT,
RESPONSE_PACKAGE_INFO_EVENT,
} from '../events-shared.js';
import * as store from './store.js';

export const initEventListenersAndStore = () => {
Expand All @@ -9,5 +15,9 @@ export const initEventListenersAndStore = () => {
store.updatePackageInfo(packageId, part, info);
});

addMessagingEventListener(EVENT_SETTINGS_CHANGED, (settings) => {
store.updateSettings(settings);
});

dispatchEvent(READY_EVENT);
};
11 changes: 11 additions & 0 deletions src/events-shared.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import browser from './browser';

const overlayPrefix = 'overlay-';
export const REQUEST_PACKAGE_INFO_EVENT = overlayPrefix + 'REQUEST_PACKAGE_INFO_EVENT';
export const RESPONSE_PACKAGE_INFO_EVENT = overlayPrefix + 'RESPONSE_PACKAGE_INFO_EVENT';
export const READY_EVENT = overlayPrefix + 'READY_EVENT';
export const CONTENT_PORT_CONNECTION = overlayPrefix + 'content-script';
export const EVENT_SETTINGS_CHANGED = overlayPrefix + 'EVENT_SETTINGS_CHANGED';

export const dispatchEvent = (type, detail) => {
window.postMessage({ type, detail });
Expand All @@ -15,3 +18,11 @@ export const addMessagingEventListener = (type, callback) => {
}
});
};

export const sendMessageToAllTabs = (type, detail) => {
browser.tabs.query({}, (tabs) => {
tabs.forEach((tab) => {
browser.tabs.sendMessage(tab.id, { type, detail });
});
});
};
6 changes: 6 additions & 0 deletions src/manifest.chrome.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
"background": {
"service_worker": "background.js"
},
"action": {
"default_icon": "icons/icon_48.png",
"default_title": "Overlay",
"default_popup": "popup/index.html"
},
"web_accessible_resources": [
{
"resources": ["custom-elements.js", "custom-elements.css"],
Expand All @@ -30,5 +35,6 @@
"externally_connectable": {
"matches": ["*://stackoverflow.com/*"]
},
"permissions": ["storage"],
"manifest_version": 3
}
14 changes: 13 additions & 1 deletion src/manifest.firefox.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
"name": "Overlay",
"version": "{{EXTENSION_VERSION}}",
"description": "{{EXTENSION_DESCRIPTION}}",
"permissions": ["https://deps.dev/*", "https://debricked.com/*", "https://socket.dev/*", "https://snyk.io/*", "https://openbase.com/*"],
"permissions": [
"storage",
"https://deps.dev/*",
"https://debricked.com/*",
"https://socket.dev/*",
"https://snyk.io/*",
"https://openbase.com/*"
],
"icons": {
"48": "icons/icon_48.png"
},
Expand All @@ -15,6 +22,11 @@
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icons/icon_48.png",
"default_title": "Overlay",
"default_popup": "popup/index.html"
},
"web_accessible_resources": ["custom-elements.js", "custom-elements.css"],
"manifest_version": 2
}
33 changes: 33 additions & 0 deletions src/popup/Popup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<main>
<div v-for="(_, key) in advisories">Show {{ key }} <input type="checkbox" v-model="advisories[key]" /></div>
</main>
</template>

<script>
import { defineComponent, toRaw } from 'vue';
import * as storage from '../storage';
import { sendEventSettingsChanged } from './popup-events';
export default defineComponent({
data() {
return {
advisories: {},
};
},
mounted() {
storage.getAllAdvisoriesSettings().then((settings) => (this.advisories = settings));
},
watch: {
advisories: {
handler(advisories) {
storage.setAllAdvisoriesSettings(toRaw(advisories)).then(sendEventSettingsChanged);
},
deep: true,
immediate: false,
},
},
});
</script>

<style scoped></style>
14 changes: 14 additions & 0 deletions src/popup/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>

<body>
<div id="app"></div>
<script type="module" src="/main.js"></script>
</body>
</html>
4 changes: 4 additions & 0 deletions src/popup/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createApp } from 'vue';
import Popup from './Popup.vue';

createApp(Popup).mount('#app');
5 changes: 5 additions & 0 deletions src/popup/popup-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { EVENT_SETTINGS_CHANGED, sendMessageToAllTabs } from '../events-shared';

export const sendEventSettingsChanged = () => {
sendMessageToAllTabs(EVENT_SETTINGS_CHANGED);
};
Loading

0 comments on commit 5a77ae4

Please sign in to comment.