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 (
+
+
+
+
+ );
+};
+
+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"
/>
-
+
+
+
+
diff --git a/packages/twenty-chrome-extension/src/options/modules/ui/display/loader/components/Loader.tsx b/packages/twenty-chrome-extension/src/options/modules/ui/display/loader/components/Loader.tsx
new file mode 100644
index 000000000..15cc03852
--- /dev/null
+++ b/packages/twenty-chrome-extension/src/options/modules/ui/display/loader/components/Loader.tsx
@@ -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 = () => (
+
+
+
+);
diff --git a/packages/twenty-front/tsconfig.json b/packages/twenty-front/tsconfig.json
index d45654692..a726af127 100644
--- a/packages/twenty-front/tsconfig.json
+++ b/packages/twenty-front/tsconfig.json
@@ -39,5 +39,5 @@
{
"path": "./tsconfig.spec.json"
}
- ]
+ ],
}