Files
twenty/packages/twenty-chrome-extension/src/options/Sidepanel.tsx
Paul Rastoin 9ad8287dbc [REFACTOR] twenty-shared multi barrel and CJS/ESM build with preconstruct (#11083)
# Introduction

In this PR we've migrated `twenty-shared` from a `vite` app
[libary-mode](https://vite.dev/guide/build#library-mode) to a
[preconstruct](https://preconstruct.tools/) "atomic" application ( in
the future would like to introduce preconstruct to handle of all our
atomic dependencies such as `twenty-emails` `twenty-ui` etc it will be
integrated at the monorepo's root directly, would be to invasive in the
first, starting incremental via `twenty-shared`)

For more information regarding the motivations please refer to nor:
- https://github.com/twentyhq/core-team-issues/issues/587
-
https://github.com/twentyhq/core-team-issues/issues/281#issuecomment-2630949682

close https://github.com/twentyhq/core-team-issues/issues/589
close https://github.com/twentyhq/core-team-issues/issues/590

## How to test
In order to ease the review this PR will ship all the codegen at the
very end, the actual meaning full diff is `+2,411 −114`
In order to migrate existing dependent packages to `twenty-shared` multi
barrel new arch you need to run in local:
```sh
yarn tsx packages/twenty-shared/scripts/migrateFromSingleToMultiBarrelImport.ts && \
npx nx run-many -t lint --fix -p twenty-front twenty-ui twenty-server twenty-emails twenty-shared twenty-zapier
```
Note that `migrateFromSingleToMultiBarrelImport` is idempotent, it's atm
included in the PR but should not be merged. ( such as codegen will be
added before merging this script will be removed )

## Misc
- related opened issue preconstruct
https://github.com/preconstruct/preconstruct/issues/617

## Closed related PR
- https://github.com/twentyhq/twenty/pull/11028
- https://github.com/twentyhq/twenty/pull/10993
- https://github.com/twentyhq/twenty/pull/10960

## Upcoming enhancement: ( in others dedicated PRs )
- 1/ refactor generate barrel to export atomic module instead of `*`
- 2/ generate barrel own package with several files and tests
- 3/ Migration twenty-ui the same way
- 4/ Use `preconstruct` at monorepo global level

## Conclusion
As always any suggestions are welcomed !
2025-03-22 19:16:06 +01:00

174 lines
4.5 KiB
TypeScript

import styled from '@emotion/styled';
import { useCallback, useEffect, useRef, useState } from 'react';
import { MainButton } from '@/ui/input/button/MainButton';
import { isDefined } from 'twenty-shared/utils';
const StyledIframe = styled.iframe`
display: block;
width: 100%;
height: 100vh;
border: none;
`;
const StyledWrapper = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.primary};
display: flex;
height: 100vh;
justify-content: center;
`;
const StyledContainer = styled.div`
width: 400px;
height: 350px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: ${({ theme }) => theme.spacing(8)};
`;
const StyledActionContainer = styled.div`
align-items: center;
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
width: 300px;
`;
const Sidepanel = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [clientUrl, setClientUrl] = useState(
import.meta.env.VITE_FRONT_BASE_URL,
);
const iframeRef = useRef<HTMLIFrameElement>(null);
const setIframeState = useCallback(async () => {
const store = await chrome.storage.local.get([
'isAuthenticated',
'sidepanelUrl',
'clientUrl',
'accessToken',
'refreshToken',
]);
if (
store.isAuthenticated === true &&
isDefined(store.accessToken) &&
isDefined(store.refreshToken) &&
new Date(store.accessToken.expiresAt).getTime() >= Date.now()
) {
setIsAuthenticated(true);
if (isDefined(store.sidepanelUrl)) {
if (isDefined(store.clientUrl)) {
setClientUrl(`${store.clientUrl}${store.sidepanelUrl}`);
} else {
setClientUrl(
`${import.meta.env.VITE_FRONT_BASE_URL}${store.sidepanelUrl}`,
);
}
}
} else {
chrome.storage.local.set({ isAuthenticated: false });
if (isDefined(store.clientUrl)) {
setClientUrl(store.clientUrl);
}
}
}, [setClientUrl]);
useEffect(() => {
void setIframeState();
}, [setIframeState]);
useEffect(() => {
window.addEventListener('message', async (event) => {
const store = await chrome.storage.local.get([
'clientUrl',
'accessToken',
'refreshToken',
]);
const clientUrl = isDefined(store.clientUrl)
? store.clientUrl
: import.meta.env.VITE_FRONT_BASE_URL;
if (
isDefined(store.accessToken) &&
isDefined(store.refreshToken) &&
event.origin === clientUrl &&
event.data === 'loaded'
) {
event.source?.postMessage(
{
type: 'tokens',
value: {
accessToken: {
token: store.accessToken.token,
expiresAt: store.accessToken.expiresAt,
},
refreshToken: {
token: store.refreshToken.token,
expiresAt: store.refreshToken.expiresAt,
},
},
},
clientUrl,
);
}
});
}, []);
useEffect(() => {
chrome.storage.local.onChanged.addListener(async (updatedStore) => {
if (isDefined(updatedStore.isAuthenticated)) {
if (updatedStore.isAuthenticated.newValue === true) {
setIframeState();
}
}
if (isDefined(updatedStore.sidepanelUrl)) {
if (isDefined(updatedStore.sidepanelUrl.newValue)) {
const store = await chrome.storage.local.get(['clientUrl']);
const clientUrl = isDefined(store.clientUrl)
? store.clientUrl
: import.meta.env.VITE_FRONT_BASE_URL;
iframeRef.current?.contentWindow?.postMessage(
{
type: 'navigate',
value: updatedStore.sidepanelUrl.newValue,
},
clientUrl,
);
}
}
});
}, [setIframeState]);
return isAuthenticated ? (
<StyledIframe
ref={iframeRef}
title="twenty-website"
src={clientUrl}
></StyledIframe>
) : (
<StyledWrapper>
<StyledContainer>
<img src="/logo/32-32.svg" alt="twenty-logo" height={40} width={40} />
<StyledActionContainer>
<MainButton
title="Connect your account"
fullWidth
onClick={() => {
window.open(clientUrl, '_blank');
}}
/>
</StyledActionContainer>
</StyledContainer>
</StyledWrapper>
);
};
export default Sidepanel;