Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: user has to login every time chrome sidepanel is opened #5544

Merged
merged 37 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2451b44
remove cache dir
AdityaPimpalkar May 22, 2024
074111b
change host permissions to only accept http connection
AdityaPimpalkar May 22, 2024
7a0ba5c
remove oauth code and add cookies onChanged listner
AdityaPimpalkar May 22, 2024
6951aea
refacto: seperate url inputs from sidepanel
AdityaPimpalkar May 22, 2024
287e77d
move input urls to new settings page
AdityaPimpalkar May 22, 2024
ebaec87
add local storage listeners to change state
AdityaPimpalkar May 22, 2024
9a5c743
define inital state for auth tokens
AdityaPimpalkar May 22, 2024
b96e66a
add comment for changing host permission
AdityaPimpalkar May 22, 2024
b608480
Merge branch 'remove-oauth' into sidepanel-multi-login-fix
AdityaPimpalkar May 22, 2024
46053be
WindowEventEffect
AdityaPimpalkar May 23, 2024
acaa2ef
update sidepanelUrl
AdityaPimpalkar May 23, 2024
92ca1b8
window listners on sidepanel
AdityaPimpalkar May 23, 2024
e9c1c3e
lint fix
AdityaPimpalkar May 23, 2024
c374efd
navigation fix
AdityaPimpalkar May 23, 2024
7911d4c
state cleanup
AdityaPimpalkar May 23, 2024
3cc6bb0
lint fix
AdityaPimpalkar May 23, 2024
31eda67
add chromeExtensionId to client config
AdityaPimpalkar May 23, 2024
979fe16
add chromeExtensionId state to frontend
AdityaPimpalkar May 23, 2024
67255e9
add isDefined checks
AdityaPimpalkar May 23, 2024
34a1b1f
Merge branch 'main' into sidepanel-multi-login-fix
AdityaPimpalkar May 24, 2024
b74d372
remove renewToken mutation
AdityaPimpalkar May 27, 2024
0da579d
window event provider
AdityaPimpalkar May 27, 2024
00df4a8
add accessToken expiry condition on sidepanel
AdityaPimpalkar May 27, 2024
fe014b8
lint fix
AdityaPimpalkar May 27, 2024
1a986c0
button mount fix
AdityaPimpalkar May 27, 2024
02984da
remove dependency
AdityaPimpalkar May 27, 2024
436e2e5
lint fix
AdityaPimpalkar May 27, 2024
b544978
build fix on project.json
AdityaPimpalkar May 28, 2024
75eebb3
add settings button
AdityaPimpalkar May 28, 2024
9df6169
sidepanel internal navigation fix
AdityaPimpalkar May 28, 2024
3b342c7
split window event to provider and effect
AdityaPimpalkar May 28, 2024
41cbf1f
add sidepanel navigation
AdityaPimpalkar May 28, 2024
b65e195
read cookies from clientUrl upon cookie change
AdityaPimpalkar May 28, 2024
8a840f4
lint fix
AdityaPimpalkar May 28, 2024
5687625
Merge branch 'main' into sidepanel-multi-login-fix
AdityaPimpalkar May 28, 2024
e9a7815
yarn lock
AdityaPimpalkar May 28, 2024
0e1ba39
rename WindowEvent to ChromeExtensionSidecar
AdityaPimpalkar May 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
"class-transformer": "^0.5.1",
"clsx": "^2.1.1",
"cross-env": "^7.0.3",
"crypto-js": "^4.2.0",
"danger-plugin-todos": "^1.3.1",
"dataloader": "^2.2.2",
"date-fns": "^2.30.0",
Expand Down Expand Up @@ -241,8 +240,6 @@
"@types/better-sqlite3": "^7.6.8",
"@types/bytes": "^3.1.1",
"@types/chrome": "^0.0.267",
"@types/crypto-js": "^4.2.2",
"@types/dagre": "^0.7.52",
"@types/deep-equal": "^1.0.1",
"@types/express": "^4.17.13",
"@types/graphql-fields": "^1.3.6",
Expand Down
7 changes: 4 additions & 3 deletions packages/twenty-chrome-extension/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
}
},
"start": {
"executor": "@nx/vite:dev-server",
"executor": "nx:run-commands",
"dependsOn": ["build"],
"options": {
"buildTarget": "twenty-chrome-extension:build",
"hmr": true
"cwd": "packages/twenty-chrome-extension",
"command": "VITE_MODE=development vite"
}
},
"preview": {
Expand Down
21 changes: 9 additions & 12 deletions packages/twenty-chrome-extension/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,6 @@ chrome.runtime.onMessage.addListener((message, _, sendResponse) => {
});
break;
}
case 'changeSidepanelUrl': {
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
if (isDefined(tab) && isDefined(tab.id)) {
chrome.tabs.sendMessage(tab.id, {
action: 'changeSidepanelUrl',
message,
});
}
});
break;
}
default:
break;
}
Expand Down Expand Up @@ -82,7 +71,15 @@ const setTokenStateFromCookie = (cookie: string) => {

chrome.cookies.onChanged.addListener(async ({ cookie }) => {
if (cookie.name === 'tokenPair') {
setTokenStateFromCookie(cookie.value);
const store = await chrome.storage.local.get(['clientUrl']);
const clientUrl = isDefined(store.clientUrl)
? store.clientUrl
: import.meta.env.VITE_FRONT_BASE_URL;
chrome.cookies.get({ name: 'tokenPair', url: `${clientUrl}` }, (cookie) => {
if (isDefined(cookie)) {
setTokenStateFromCookie(cookie.value);
}
});
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const createDefaultButton = (
padding: '0 1rem',
cursor: 'pointer',
height: '32px',
width: 'max-content',
};

Object.assign(div.style, divStyles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ export const addCompany = async () => {
const companyId = await createCompany(companyInputData);

if (isDefined(companyId)) {
await changeSidePanelUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}/object/company/${companyId}`,
);
await changeSidePanelUrl(`/object/company/${companyId}`);
}

return companyId;
Expand All @@ -86,16 +84,15 @@ export const addCompany = async () => {
export const insertButtonForCompany = async () => {
const companyButtonDiv = createDefaultButton('twenty-company-btn');

const parentDiv: HTMLDivElement | null = document.querySelector(
'.org-top-card-primary-actions__inner',
const companyDiv: HTMLDivElement | null = document.querySelector(
'.org-top-card__primary-content',
);

if (isDefined(parentDiv)) {
if (isDefined(companyDiv)) {
Object.assign(companyButtonDiv.style, {
marginLeft: '.8rem',
marginTop: '.4rem',
marginTop: '.8rem',
});
parentDiv.prepend(companyButtonDiv);
companyDiv.parentElement?.append(companyButtonDiv);
}

const companyButtonSpan = companyButtonDiv.getElementsByTagName('span')[0];
Expand All @@ -104,19 +101,16 @@ export const insertButtonForCompany = async () => {
const openCompanyOnSidePanel = (companyId: string) => {
companyButtonSpan.textContent = 'View in Twenty';
companyButtonDiv.onClickHandler(async () => {
await changeSidePanelUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}/object/company/${companyId}`,
);
await changeSidePanelUrl(`/object/company/${companyId}`);
chrome.runtime.sendMessage({ action: 'openSidepanel' });
});
};

if (isDefined(company)) {
await changeSidePanelUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}/object/company/${company.id}`,
);
await changeSidePanelUrl(`/object/company/${company.id}`);
if (isDefined(company.id)) openCompanyOnSidePanel(company.id);
} else {
await changeSidePanelUrl(`/objects/companies`);
companyButtonSpan.textContent = 'Add to Twenty';

companyButtonDiv.onClickHandler(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ export const addPerson = async () => {
const personId = await createPerson(personData);

if (isDefined(personId)) {
await changeSidePanelUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}/object/person/${personId}`,
);
await changeSidePanelUrl(`/object/person/${personId}`);
}

return personId;
Expand All @@ -98,15 +96,13 @@ export const insertButtonForPerson = async () => {
const personButtonDiv = createDefaultButton('twenty-person-btn');

if (isDefined(personButtonDiv)) {
const addedProfileDiv: HTMLDivElement | null = document.querySelector(
'.pv-top-card-v2-ctas__custom',
);
const addedProfileDiv = document.querySelector('.artdeco-card > .ph5');

if (isDefined(addedProfileDiv)) {
Object.assign(personButtonDiv.style, {
marginRight: '.8rem',
marginTop: '.8rem',
});
addedProfileDiv.prepend(personButtonDiv);
addedProfileDiv.append(personButtonDiv);
}

const personButtonSpan = personButtonDiv.getElementsByTagName('span')[0];
Expand All @@ -115,19 +111,16 @@ export const insertButtonForPerson = async () => {
const openPersonOnSidePanel = (personId: string) => {
personButtonSpan.textContent = 'View in Twenty';
personButtonDiv.onClickHandler(async () => {
await changeSidePanelUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}/object/person/${personId}`,
);
await changeSidePanelUrl(`/object/person/${personId}`);
chrome.runtime.sendMessage({ action: 'openSidepanel' });
});
};

if (isDefined(person)) {
await changeSidePanelUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}/object/person/${person.id}`,
);
await changeSidePanelUrl(`/object/person/${person.id}`);
if (isDefined(person.id)) openPersonOnSidePanel(person.id);
} else {
await changeSidePanelUrl(`/objects/people`);
personButtonSpan.textContent = 'Add to Twenty';
personButtonDiv.onClickHandler(async () => {
personButtonSpan.textContent = 'Saving...';
Expand Down
27 changes: 19 additions & 8 deletions packages/twenty-chrome-extension/src/contentScript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,31 @@ import { isDefined } from '~/utils/isDefined';
// Inject buttons into the DOM when SPA is reloaded on the resource url.
// e.g. reload the page when on https://www.linkedin.com/in/mabdullahabaid/
// await insertButtonForCompany();
(async () => {
await insertButtonForCompany();
await insertButtonForPerson();
})();

const companyRoute = /^https?:\/\/(?:www\.)?linkedin\.com\/company(?:\/\S+)?/;
const personRoute = /^https?:\/\/(?:www\.)?linkedin\.com\/in(?:\/\S+)?/;

const executeScript = async () => {
const loc = window.location.href;
switch (true) {
case companyRoute.test(loc):
await insertButtonForCompany();
break;
case personRoute.test(loc):
await insertButtonForPerson();
break;
default:
break;
}
};

// The content script gets executed upon load, so the the content script is executed when a user visits https://www.linkedin.com/feed/.
// However, there would never be another reload in a single page application unless triggered manually.
// Therefore, if the user navigates to a person or a company page, we must manually re-execute the content script to create the "Add to Twenty" button.
// e.g. create "Add to Twenty" button when a user navigates to https://www.linkedin.com/in/mabdullahabaid/ from https://www.linkedin.com/feed/
chrome.runtime.onMessage.addListener(async (message, _, sendResponse) => {
if (message.action === 'executeContentScript') {
await insertButtonForCompany();
await insertButtonForPerson();
await executeScript();
}

sendResponse('Executing!');
Expand All @@ -26,8 +38,7 @@ chrome.runtime.onMessage.addListener(async (message, _, sendResponse) => {
chrome.storage.local.onChanged.addListener(async (store) => {
if (isDefined(store.accessToken)) {
if (isDefined(store.accessToken.newValue)) {
await insertButtonForCompany();
await insertButtonForPerson();
await executeScript();
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { isDefined } from '~/utils/isDefined';

const btn = document.getElementById('twenty-settings-btn');
if (!isDefined(btn)) {
const div = document.createElement('div');
const img = document.createElement('img');
img.src =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
img.height = 20;
img.width = 20;
img.alt = 'Twenty logo';

// Write universal styles for the button
const divStyles = {
border: '1px solid black',
borderRadius: '50%',
backgroundColor: 'black',
color: 'white',
fontWeight: '600',
fontSize: '1.5rem',
display: 'flex',
alignItems: 'center',
gap: '5px',
justifyContent: 'center',
padding: '0 1rem',
cursor: 'pointer',
height: '50px',
width: '50px',
position: 'fixed',
bottom: '80px',
right: '20px',
zIndex: '9999999999999999999999999',
};

div.addEventListener('mouseenter', () => {
const hoverStyles = {
//eslint-disable-next-line @nx/workspace-no-hardcoded-colors
backgroundColor: '#5e5e5e',
//eslint-disable-next-line @nx/workspace-no-hardcoded-colors
borderColor: '#5e5e5e',
};
Object.assign(div.style, hoverStyles);
});

div.addEventListener('mouseleave', () => {
Object.assign(div.style, divStyles);
});

div.onclick = async () => {
chrome.runtime.sendMessage({ action: 'openSidepanel' });
chrome.storage.local.set({ navigateSidepanel: 'settings' });
};

div.appendChild(img);

Object.assign(div.style, divStyles);

document.body.appendChild(div);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { isDefined } from '~/utils/isDefined';

const changeSidePanelUrl = async (url: string) => {
const { tab: activeTab } = await chrome.runtime.sendMessage({
action: 'getActiveTab',
});
if (isDefined(activeTab) && isDefined(url)) {
chrome.storage.local.set({ [`sidepanelUrl_${activeTab.id}`]: url });
chrome.runtime.sendMessage({
action: 'changeSidepanelUrl',
message: { url },
});
if (isDefined(url)) {
chrome.storage.local.set({ navigateSidepanel: 'sidepanel' });
// we first clear the sidepanelUrl to trigger the onchange listener on sidepanel
// which will pass the post meessage to handle internal navigation of iframe
chrome.storage.local.set({ sidepanelUrl: '' });
chrome.storage.local.set({ sidepanelUrl: url });
}
};

Expand Down
5 changes: 4 additions & 1 deletion packages/twenty-chrome-extension/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export default defineManifest({
content_scripts: [
{
matches: ['https://www.linkedin.com/*'],
js: ['src/contentScript/index.ts'],
js: [
'src/contentScript/index.ts',
'src/contentScript/insertSettingsButton.ts',
],
run_at: 'document_end',
},
],
Expand Down
42 changes: 42 additions & 0 deletions packages/twenty-chrome-extension/src/options/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect, useState } from 'react';

import Settings from '~/options/Settings';
import Sidepanel from '~/options/Sidepanel';
import { isDefined } from '~/utils/isDefined';

const App = () => {
const [currentScreen, setCurrentScreen] = useState('');

useEffect(() => {
const setCurrentScreenState = async () => {
const store = await chrome.storage.local.get(['navigateSidepanel']);
if (isDefined(store.navigateSidepanel)) {
setCurrentScreen(store.navigateSidepanel);
}
};

setCurrentScreenState();
}, []);

useEffect(() => {
chrome.storage.local.onChanged.addListener((updatedStore) => {
if (
isDefined(updatedStore.navigateSidepanel) &&
isDefined(updatedStore.navigateSidepanel.newValue)
) {
setCurrentScreen(updatedStore.navigateSidepanel.newValue);
}
});
}, [setCurrentScreen]);

switch (currentScreen) {
case 'sidepanel':
return <Sidepanel />;
case 'settings':
return <Settings />;
default:
return <Settings />;
}
};

export default App;
Loading
Loading