diff --git a/packages/twenty-chrome-extension/loading.html b/packages/twenty-chrome-extension/loading.html new file mode 100644 index 000000000..b286e663d --- /dev/null +++ b/packages/twenty-chrome-extension/loading.html @@ -0,0 +1,12 @@ + + + + + + Twenty + + +
+ + + diff --git a/packages/twenty-chrome-extension/public/light-noise.png b/packages/twenty-chrome-extension/public/light-noise.png new file mode 100644 index 000000000..d7b3bc2c0 Binary files /dev/null and b/packages/twenty-chrome-extension/public/light-noise.png differ diff --git a/packages/twenty-chrome-extension/src/background/index.ts b/packages/twenty-chrome-extension/src/background/index.ts index 36f2a1179..271224ee0 100644 --- a/packages/twenty-chrome-extension/src/background/index.ts +++ b/packages/twenty-chrome-extension/src/background/index.ts @@ -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. diff --git a/packages/twenty-chrome-extension/src/contentScript/index.ts b/packages/twenty-chrome-extension/src/contentScript/index.ts index a541e0144..0e81cc94c 100644 --- a/packages/twenty-chrome-extension/src/contentScript/index.ts +++ b/packages/twenty-chrome-extension/src/contentScript/index.ts @@ -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); +}; diff --git a/packages/twenty-chrome-extension/src/manifest.ts b/packages/twenty-chrome-extension/src/manifest.ts index dc08d0bca..dd3896ba7 100644 --- a/packages/twenty-chrome-extension/src/manifest.ts +++ b/packages/twenty-chrome-extension/src/manifest.ts @@ -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/*`], + }, }); diff --git a/packages/twenty-chrome-extension/src/options/Loading.tsx b/packages/twenty-chrome-extension/src/options/Loading.tsx new file mode 100644 index 000000000..1fde24f2b --- /dev/null +++ b/packages/twenty-chrome-extension/src/options/Loading.tsx @@ -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 ( + + twenty-logo + + + ); +}; + +export default Loading; diff --git a/packages/twenty-chrome-extension/src/options/index.tsx b/packages/twenty-chrome-extension/src/options/index.tsx index dde380ebf..e7920fc9e 100644 --- a/packages/twenty-chrome-extension/src/options/index.tsx +++ b/packages/twenty-chrome-extension/src/options/index.tsx @@ -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( - + , ); diff --git a/packages/twenty-chrome-extension/src/options/loading-index.tsx b/packages/twenty-chrome-extension/src/options/loading-index.tsx new file mode 100644 index 000000000..46f2193fe --- /dev/null +++ b/packages/twenty-chrome-extension/src/options/loading-index.tsx @@ -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( + + + + + , +); + +declare module '@emotion/react' { + export interface Theme extends ThemeType {} +} diff --git a/packages/twenty-chrome-extension/src/options/modules/api-key/components/ApiKeyForm.tsx b/packages/twenty-chrome-extension/src/options/modules/api-key/components/ApiKeyForm.tsx index 88e051972..524dc0c1f 100644 --- a/packages/twenty-chrome-extension/src/options/modules/api-key/components/ApiKeyForm.tsx +++ b/packages/twenty-chrome-extension/src/options/modules/api-key/components/ApiKeyForm.tsx @@ -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 ( - + - { onChange={setApiKey} placeholder="My API key" /> -