feat: iframe addition (chrome-extension) (#4418)

* toggle iframe addition

* React UI init

* remove files

* loading state files

* render iframe logic

* remove event

* build fix

* WIP

* Ok

* Cleaned

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Aditya Pimpalkar
2024-03-15 15:36:53 +00:00
committed by GitHub
parent c083bb15cd
commit 638a12c571
11 changed files with 206 additions and 26 deletions

View File

@@ -0,0 +1,12 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/icons/android/android-launchericon-48-48.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Twenty</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/options/loading-index.tsx"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -8,8 +8,8 @@ chrome.runtime.onInstalled.addListener((details) => {
});
// Open options page when extension icon is clicked.
chrome.action.onClicked.addListener(() => {
openOptionsPage();
chrome.action.onClicked.addListener((tab) => {
chrome.tabs.sendMessage(tab.id ?? 0, { action: 'TOGGLE' });
});
// This listens for an event from other parts of the extension, such as the content script, and performs the required tasks.

View File

@@ -16,5 +16,55 @@ chrome.runtime.onMessage.addListener((message, _, sendResponse) => {
insertButtonForPerson();
}
if (message.action === 'TOGGLE') {
toggle();
}
sendResponse('Executing!');
});
const createIframe = () => {
const iframe = document.createElement('iframe');
iframe.style.background = 'lightgrey';
iframe.style.height = '100vh';
iframe.style.width = '400px';
iframe.style.position = 'fixed';
iframe.style.top = '0px';
iframe.style.right = '-400px';
iframe.style.zIndex = '9000000000000000000';
iframe.style.transition = 'ease-in-out 0.3s';
return iframe;
};
const handleContentIframeLoadComplete = () => {
//If the pop-out window is already open then we replace loading iframe with our content iframe
if (loadingIframe.style.right === '0px') contentIframe.style.right = '0px';
loadingIframe.style.display = 'none';
contentIframe.style.display = 'block';
};
//Creating one iframe where we are loading our front end in the background
const contentIframe = createIframe();
contentIframe.style.display = 'none';
contentIframe.src = `${import.meta.env.VITE_FRONT_BASE_URL}`;
contentIframe.onload = handleContentIframeLoadComplete;
//Creating this iframe to show as a loading state until the above iframe loads completely
const loadingIframe = createIframe();
loadingIframe.src = chrome.runtime.getURL('loading.html');
document.body.appendChild(loadingIframe);
document.body.appendChild(contentIframe);
const toggleIframe = (iframe: HTMLIFrameElement) => {
if (iframe.style.right === '-400px' && iframe.style.display !== 'none') {
iframe.style.right = '0px';
} else if (iframe.style.right === '0px' && iframe.style.display !== 'none') {
iframe.style.right = '-400px';
}
};
const toggle = () => {
toggleIframe(loadingIframe);
toggleIframe(contentIframe);
};

View File

@@ -16,6 +16,7 @@ export default defineManifest({
action: {},
//TODO: change this to a documenation page
options_page: 'options.html',
background: {
@@ -34,4 +35,8 @@ export default defineManifest({
permissions: ['activeTab', 'storage'],
host_permissions: ['https://www.linkedin.com/*'],
externally_connectable: {
matches: [`https://app.twenty.com/*`, `http://localhost:3001/*`],
},
});

View File

@@ -0,0 +1,24 @@
import styled from '@emotion/styled';
import { Loader } from '@/ui/display/loader/components/Loader';
const StyledContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.noisy};
display: flex;
flex-direction: column;
height: 100vh;
gap: ${({ theme }) => theme.spacing(2)};
justify-content: center;
`;
const Loading = () => {
return (
<StyledContainer>
<img src="/logo/32-32.svg" alt="twenty-logo" height={64} width={64} />
<Loader />
</StyledContainer>
);
};
export default Loading;

View File

@@ -3,14 +3,14 @@ import ReactDOM from 'react-dom/client';
import { AppThemeProvider } from '@/ui/theme/components/AppThemeProvider';
import { ThemeType } from '@/ui/theme/constants/ThemeLight';
import App from '~/options/Options';
import Options from '~/options/Options';
import '~/index.css';
ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(
<AppThemeProvider>
<React.StrictMode>
<App />
<Options />
</React.StrictMode>
</AppThemeProvider>,
);

View File

@@ -0,0 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { AppThemeProvider } from '@/ui/theme/components/AppThemeProvider';
import { ThemeType } from '@/ui/theme/constants/ThemeLight';
import Loading from '~/options/Loading';
import '~/index.css';
ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(
<AppThemeProvider>
<React.StrictMode>
<Loading />
</React.StrictMode>
</AppThemeProvider>,
);
declare module '@emotion/react' {
export interface Theme extends ThemeType {}
}

View File

@@ -23,7 +23,11 @@ const StyledHeader = styled.header`
text-align: center;
`;
const StyledImg = styled.img``;
const StyledImgLogo = styled.img`
&:hover {
cursor: pointer;
}
`;
const StyledMain = styled.main`
margin-bottom: ${({ theme }) => theme.spacing(8)};
@@ -51,6 +55,13 @@ const StyledSection = styled.div<{ showSection: boolean }>`
max-height: ${({ showSection }) => (showSection ? '200px' : '0')};
`;
const StyledButtonHorizontalContainer = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(4)};
width: 100%;
`;
export const ApiKeyForm = () => {
const [apiKey, setApiKey] = useState('');
const [route, setRoute] = useState('');
@@ -73,10 +84,6 @@ export const ApiKeyForm = () => {
void getState();
}, []);
useEffect(() => {
chrome.storage.local.set({ apiKey });
}, [apiKey]);
useEffect(() => {
if (import.meta.env.VITE_SERVER_BASE_URL !== route) {
chrome.storage.local.set({ serverBaseUrl: route });
@@ -85,10 +92,18 @@ export const ApiKeyForm = () => {
}
}, [route]);
const handleValidateKey = () => {
chrome.storage.local.set({ apiKey });
window.close();
};
const handleGenerateClick = () => {
window.open(
`${import.meta.env.VITE_FRONT_BASE_URL}/settings/developers/api-keys`,
);
window.open(`${import.meta.env.VITE_FRONT_BASE_URL}/settings/developers`);
};
const handleGoToTwenty = () => {
window.open(`${import.meta.env.VITE_FRONT_BASE_URL}`);
};
const handleToggle = () => {
@@ -98,9 +113,12 @@ export const ApiKeyForm = () => {
return (
<StyledContainer isToggleOn={showSection}>
<StyledHeader>
<StyledImg src="/logo/32-32.svg" alt="Twenty Logo" />
<StyledImgLogo
src="/logo/32-32.svg"
alt="Twenty Logo"
onClick={handleGoToTwenty}
/>
</StyledHeader>
<StyledMain>
<H2Title
title="Connect your account"
@@ -112,17 +130,30 @@ export const ApiKeyForm = () => {
onChange={setApiKey}
placeholder="My API key"
/>
<Button
title="Generate a key"
fullWidth={false}
variant="primary"
accent="default"
size="small"
position="standalone"
soon={false}
disabled={false}
onClick={handleGenerateClick}
/>
<StyledButtonHorizontalContainer>
<Button
title="Generate a key"
fullWidth={true}
variant="primary"
accent="default"
size="small"
position="standalone"
soon={false}
disabled={false}
onClick={handleGenerateClick}
/>
<Button
title="Validate key"
fullWidth={true}
variant="primary"
accent="default"
size="small"
position="standalone"
soon={false}
disabled={apiKey === ''}
onClick={handleValidateKey}
/>
</StyledButtonHorizontalContainer>
</StyledMain>
<StyledFooter>

View File

@@ -0,0 +1,38 @@
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
const StyledLoaderContainer = styled.div`
justify-content: center;
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
width: ${({ theme }) => theme.spacing(6)};
height: ${({ theme }) => theme.spacing(3)};
border-radius: ${({ theme }) => theme.border.radius.pill};
border: 1px solid ${({ theme }) => theme.font.color.tertiary};
overflow: hidden;
`;
const StyledLoader = styled(motion.div)`
background-color: ${({ theme }) => theme.font.color.tertiary};
border-radius: ${({ theme }) => theme.border.radius.pill};
height: 8px;
width: 8px;
`;
export const Loader = () => (
<StyledLoaderContainer>
<StyledLoader
animate={{
x: [-16, 0, 16],
width: [8, 12, 8],
height: [8, 2, 8],
}}
transition={{
duration: 0.8,
times: [0, 0.15, 0.3],
repeat: Infinity,
}}
/>
</StyledLoaderContainer>
);

View File

@@ -39,5 +39,5 @@
{
"path": "./tsconfig.spec.json"
}
]
],
}