Skip to content

Commit

Permalink
Web Extension - Added toggle + finalized build MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
paustint committed Jul 27, 2024
1 parent 13518a5 commit c472e08
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 95 deletions.
9 changes: 6 additions & 3 deletions apps/jetstream-web-extension/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@
},
"configurations": {
"development": {
"extractLicenses": false,
"fileReplacements": [],
"optimization": false,
"outputHashing": "none",
"sourceMap": true,
"vendorChunk": true
"namedChunks": false,
"extractLicenses": false,
"vendorChunk": false
},
"production": {
"fileReplacements": [],
"optimization": false,
"optimization": true,
"outputHashing": "none",
"sourceMap": true,
"namedChunks": false,
Expand Down
42 changes: 33 additions & 9 deletions apps/jetstream-web-extension/src/contentScript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,37 @@ console.log('Content script loaded.', __webpack_public_path__);

const elementId = 'jetstream-app-container';

const app = document.createElement('div');
app.id = elementId;
document.body.appendChild(app);
function renderApp() {
// TODO: check storage to see if app is disabled before mounting
if (!document.getElementById(elementId)) {
const app = document.createElement('div');
app.id = elementId;
document.body.appendChild(app);
initAndRenderReact(
<AppWrapperNotJetstreamOwnedPage>
<Button />
</AppWrapperNotJetstreamOwnedPage>,
{ elementId }
);
}
}

initAndRenderReact(
<AppWrapperNotJetstreamOwnedPage>
<Button />
</AppWrapperNotJetstreamOwnedPage>,
{ elementId }
);
function destroyApp() {
const app = document.getElementById(elementId);
if (app) {
app.remove();
}
}

renderApp();

chrome.storage.onChanged.addListener((changes, area) => {
if (area === 'local' && changes.options?.newValue) {
const { enabled } = changes.options.newValue;
if (enabled) {
renderApp();
} else {
destroyApp();
}
}
});
3 changes: 3 additions & 0 deletions apps/jetstream-web-extension/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"https://*.visual.force.com/*",
"https://*.visualforce.com/*"
],
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "serviceWorker.js"
},
Expand Down
38 changes: 31 additions & 7 deletions apps/jetstream-web-extension/src/pages/popup/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
/* eslint-disable no-restricted-globals */
import { JetstreamLogoInverse } from '@jetstream/ui-core';
import { useNonInitialEffect } from '@jetstream/shared/ui-utils';
import { CheckboxToggle } from '@jetstream/ui';
import { JetstreamLogo } from '@jetstream/ui-core';
import { initAndRenderReact } from '@jetstream/web-extension-utils';
import { useEffect, useState } from 'react';
import { AppWrapperNotJetstreamOwnedPage } from '../../core/AppWrapperNotJetstreamOwnedPage';

initAndRenderReact(<Component />);

export function Component() {
function handleClick() {
console.log('click');
}
const [enabled, setEnabled] = useState(true);

useEffect(() => {
(async () => {
const storage = (await chrome.storage.local.get('options')) || {};
storage.options = storage.options || { enabled: true };
setEnabled(storage.options.enabled);
})();
}, []);

useNonInitialEffect(() => {
(async () => {
chrome.storage.local.set({ options: { enabled } });
})();
}, [enabled]);

return (
<AppWrapperNotJetstreamOwnedPage>
<JetstreamLogoInverse className="slds-p-around_x-small" width="200px" />
<button onClick={handleClick}>click me</button>
<header className="slds-m-bottom_medium">
<JetstreamLogo />
</header>
<div>
<CheckboxToggle
id="enable-extension-button"
checked={enabled}
label="Enable Page Extension"
labelHelp="If you want to disabled Jetstream, uncheck this box."
onChange={(value) => setEnabled(value)}
/>
</div>
</AppWrapperNotJetstreamOwnedPage>
);
}
10 changes: 9 additions & 1 deletion apps/jetstream-web-extension/src/pages/popup/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Jetstream</title>
<style>
body {
width: 350px;
height: 400px;
background-color: white !important;
padding: 15px;
}
</style>
</head>
<body style="width: 650px; height: 550px">
<body>
<div id="app-container"></div>
</body>
</html>
73 changes: 4 additions & 69 deletions apps/jetstream-web-extension/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,15 @@
const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const CopyWebpackPlugin = require('copy-webpack-plugin');
// const CrxLoadScriptWebpackPlugin = require('@cooby/crx-load-script-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
// const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const webpack = require('webpack');

// Nx plugins for webpack.
// @ts-expect-error withReact is complaining about the type of the config - but works on some machines just fine
module.exports = composePlugins(withNx(), withReact(), (config) => {
const isDev = config.mode === 'development';
config.devtool = isDev ? 'inline-source-map' : 'source-map';

// // @ts-expect-error typescript type is not correctly detected
// config.devServer = {
// // @ts-expect-error typescript type is not correctly detected
// ...config.devServer,
// hot: true,
// /**
// * We need devServer write files to disk,
// * But don't want it reload whole page because of the output file changes.
// */
// static: { watch: false },
// // static: true,
// /**
// * Set WebSocket url to dev-server, instead of the default `${publicPath}/ws`
// */
// client: {
// webSocketURL: 'ws://localhost:4201/ws',
// },
// /**
// * The host of the page of your script extension runs on.
// * You'll see `[webpack-dev-server] Invalid Host/Origin header` if this is not set.
// * preceding period is a wildcard for subdomains
// */
// allowedHosts: ['.salesforce.com', '.visual.force.com', '.lightning.force.com', '.cloudforce.com', '.visualforce.com'],
// devMiddleware: {
// // @ts-expect-error typescript type is not correctly detected
// ...config.devServer?.devMiddleware,
// /**
// * Write file to output folder /build, so we can execute it later.
// */
// writeToDisk: true,
// },
// };

config.entry = {
app: './src/pages/app/app.tsx',
popup: './src/pages/popup/Popup.tsx',
Expand All @@ -70,26 +34,19 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
config.optimization = {
...config.optimization,
runtimeChunk: false,
sideEffects: true,
splitChunks: false,
};
config.plugins = config.plugins || [];
config.plugins.push(
// @ts-expect-error not sure why this is saying invalid type
// TODO: do I need to remove existing plugins?
// ...(isDev
// ? [
// new CrxLoadScriptWebpackPlugin(),
// new ReactRefreshWebpackPlugin({
// overlay: false,
// }),
// ]
// : []),
// @ts-expect-error this is valid, not sure why it is complaining
new webpack.EnvironmentPlugin({
NX_PUBLIC_AUTH_AUDIENCE: 'http://getjetstream.app/app_metadata',
NX_PUBLIC_AMPLITUDE_KEY: '',
NX_PUBLIC_ROLLBAR_KEY: '',
}),
new webpack.DefinePlugin({
__IS_CHROME_EXTENSION__: true,
}),
createHtmlPagePlugin('app'),
createHtmlPagePlugin('popup'),
createHtmlPagePlugin('options'),
Expand All @@ -99,32 +56,10 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
from: 'src/manifest.json',
to: config.output.path,
force: true,
// transform: function (content, path) {
// if (!isDev) {
// return content;
// }
// /**
// * @type {chrome.runtime.ManifestV3}
// */
// const manifest = JSON.parse(content.toString());
// manifest.permissions?.push('scripting');
// manifest.web_accessible_resources?.[0].resources?.push('*.hot-update.json');
// // TODO: add versioning
// return Buffer.from(JSON.stringify(manifest, null, 2));
// // generates the manifest file using the package.json information
// // return Buffer.from(
// // JSON.stringify({
// // description: process.env.npm_package_description,
// // version: process.env.npm_package_version,
// // ...JSON.parse(content.toString()),
// // })
// // );
// },
},
],
})
);

return config;
});

Expand Down
3 changes: 3 additions & 0 deletions apps/jetstream/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
}
});
</script>
<script>
__IS_CHROME_EXTENSION__ = false;
</script>
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 1 addition & 0 deletions custom-typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare module 'worker-loader!*' {
}

declare global {
declare const __IS_CHROME_EXTENSION__: boolean;
interface Window {
// placeholder for any global properties
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ export const LoadRecordsMultiObject: FunctionComponent<LoadRecordsMultiObjectPro
</select>
</Select>
<FileOrGoogleSelector
omitGoogle={__IS_CHROME_EXTENSION__}
fileSelectorProps={{
id: 'upload-load-template',
label: 'Data File (Excel File)',
Expand Down
3 changes: 2 additions & 1 deletion libs/features/load-records/src/steps/SelectObjectAndFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ export const LoadRecordsSelectObjectAndFile: FunctionComponent<LoadRecordsSelect
onExternalIdChange,
children,
}) => {
const hasGoogleInputConfigured = !!googleApiConfig?.apiKey && !!googleApiConfig?.appId && !!googleApiConfig?.clientId;
const hasGoogleInputConfigured =
!__IS_CHROME_EXTENSION__ && !!googleApiConfig?.apiKey && !!googleApiConfig?.appId && !!googleApiConfig?.clientId;
async function handleFile({ content, filename, isPasteFromClipboard, extension }: InputReadFileContent) {
try {
const { data, headers, errors } = await parseFile(content, { onParsedMultipleWorkbooks, isPasteFromClipboard, extension });
Expand Down
9 changes: 7 additions & 2 deletions libs/shared/ui-utils/src/lib/hooks/useGoogleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import { getUseInjectScript } from './useInjectScript';
import { useNonInitialEffect } from './useNonInitialEffect';
import { useRollbar } from './useRollbar';

const useInjectScriptGapi = getUseInjectScript('https://apis.google.com/js/api.js');
const useInjectScriptGis = getUseInjectScript('https://accounts.google.com/gsi/client');
let useInjectScriptGapi: () => [boolean, boolean] = () => [false, false];
let useInjectScriptGis: () => [boolean, boolean] = () => [false, false];

if (!__IS_CHROME_EXTENSION__) {
useInjectScriptGapi = getUseInjectScript('https://apis.google.com/js/api.js');
useInjectScriptGis = getUseInjectScript('https://accounts.google.com/gsi/client');
}

let _apiLoaded = false;
let _tokenClient: Maybe<google.accounts.oauth2.TokenClient>;
Expand Down
6 changes: 4 additions & 2 deletions libs/ui/src/lib/form/file-selector/FileOrGoogleSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Fragment, FunctionComponent, lazy } from 'react';
import Tabs from '../../tabs/Tabs';
import { Fragment, FunctionComponent } from 'react';
import FileSelector, { FileSelectorProps } from './FileSelector';
import GoogleFileSelector, { GoogleFileSelectorProps } from './GoogleFileSelector';
import type { GoogleFileSelectorProps } from './GoogleFileSelector';

const GoogleFileSelector = lazy(() => import('./GoogleFileSelector'));

export interface FileOrGoogleSelectorProps {
fileSelectorProps: FileSelectorProps;
Expand Down
10 changes: 9 additions & 1 deletion libs/web-extension-utils/src/lib/extension.types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApiConnection } from '@jetstream/salesforce-api';
import { Maybe, SalesforceOrgUi } from '@jetstream/types';

export type Message = GetSfHost | GetSession | GetPageUrl | InitOrg;
export type Message = ToggleExtension | GetSfHost | GetSession | GetPageUrl | InitOrg;
export type MessageRequest = Message['request'];
export interface MessageResponse<T extends Message['response'] = Message['response']> {
data: T;
Expand All @@ -12,6 +12,14 @@ export interface SessionInfo {
key: string;
}

export interface ToggleExtension {
request: {
message: 'TOGGLE_EXTENSION';
data: boolean;
};
response: void;
}

export interface GetSfHost {
request: {
message: 'GET_SF_HOST';
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"build:landing:sitemap": "next-sitemap",
"build:docs": "cd apps/docs && yarn build",
"build:storybook": "storybook build -c libs/ui/.storybook -o dist/storybook -s node_modules/@salesforce-ux/design-system/assets/styles",
"build:web-extension:dev": "nx run jetstream-web-extension:build --configuration=development",
"build:web-extension": "nx run jetstream-web-extension:build",
"release": "dotenv release-it ${0}",
"release:build": "zx ./scripts/build-release.mjs",
Expand Down

0 comments on commit c472e08

Please sign in to comment.