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

We can pass the auth tokens to our front app via post message, which
will also allow us to pass route names to navigate on it
This commit is contained in:
Aditya Pimpalkar
2024-05-30 11:58:45 +01:00
committed by GitHub
parent d770e56e31
commit a12c1aad5e
30 changed files with 511 additions and 231 deletions

View File

@ -36,6 +36,7 @@ export const createDefaultButton = (
padding: '0 1rem',
cursor: 'pointer',
height: '32px',
width: 'max-content',
};
Object.assign(div.style, divStyles);

View File

@ -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;
@ -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];
@ -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 () => {

View File

@ -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;
@ -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];
@ -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...';

View File

@ -5,10 +5,23 @@ 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.
@ -16,8 +29,7 @@ import { isDefined } from '~/utils/isDefined';
// 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!');
@ -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();
}
}
});

View File

@ -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);
}

View File

@ -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 });
}
};