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:
42
packages/twenty-chrome-extension/src/options/App.tsx
Normal file
42
packages/twenty-chrome-extension/src/options/App.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Settings from '~/options/Settings';
|
||||
import Sidepanel from '~/options/Sidepanel';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const App = () => {
|
||||
const [currentScreen, setCurrentScreen] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const setCurrentScreenState = async () => {
|
||||
const store = await chrome.storage.local.get(['navigateSidepanel']);
|
||||
if (isDefined(store.navigateSidepanel)) {
|
||||
setCurrentScreen(store.navigateSidepanel);
|
||||
}
|
||||
};
|
||||
|
||||
setCurrentScreenState();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
chrome.storage.local.onChanged.addListener((updatedStore) => {
|
||||
if (
|
||||
isDefined(updatedStore.navigateSidepanel) &&
|
||||
isDefined(updatedStore.navigateSidepanel.newValue)
|
||||
) {
|
||||
setCurrentScreen(updatedStore.navigateSidepanel.newValue);
|
||||
}
|
||||
});
|
||||
}, [setCurrentScreen]);
|
||||
|
||||
switch (currentScreen) {
|
||||
case 'sidepanel':
|
||||
return <Sidepanel />;
|
||||
case 'settings':
|
||||
return <Settings />;
|
||||
default:
|
||||
return <Settings />;
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
@ -1,7 +1,9 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { MainButton } from '@/ui/input/button/MainButton';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { clearStore } from '~/utils/apolloClient';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
@ -34,33 +36,47 @@ const StyledActionContainer = styled.div`
|
||||
const Settings = () => {
|
||||
const [serverBaseUrl, setServerBaseUrl] = useState('');
|
||||
const [clientUrl, setClientUrl] = useState('');
|
||||
const [currentClientUrl, setCurrentClientUrl] = useState('');
|
||||
const [currentServerUrl, setCurrentServerUrl] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const getState = async () => {
|
||||
const store = await chrome.storage.local.get();
|
||||
const store = await chrome.storage.local.get([
|
||||
'serverBaseUrl',
|
||||
'clientUrl',
|
||||
]);
|
||||
if (isDefined(store.serverBaseUrl)) {
|
||||
setServerBaseUrl(store.serverBaseUrl);
|
||||
setCurrentServerUrl(store.serverBaseUrl);
|
||||
} else {
|
||||
setServerBaseUrl(import.meta.env.VITE_SERVER_BASE_URL);
|
||||
setCurrentServerUrl(import.meta.env.VITE_SERVER_BASE_URL);
|
||||
}
|
||||
|
||||
if (isDefined(store.clientUrl)) {
|
||||
setClientUrl(store.clientUrl);
|
||||
setCurrentClientUrl(store.clientUrl);
|
||||
} else {
|
||||
setClientUrl(import.meta.env.VITE_FRONT_BASE_URL);
|
||||
setCurrentClientUrl(import.meta.env.VITE_FRONT_BASE_URL);
|
||||
}
|
||||
};
|
||||
void getState();
|
||||
}, []);
|
||||
|
||||
const handleBaseUrlChange = (value: string) => {
|
||||
setServerBaseUrl(value);
|
||||
chrome.storage.local.set({ serverBaseUrl: value });
|
||||
const handleSettingsChange = () => {
|
||||
chrome.storage.local.set({
|
||||
serverBaseUrl,
|
||||
clientUrl,
|
||||
navigateSidepanel: 'sidepanel',
|
||||
});
|
||||
clearStore();
|
||||
};
|
||||
|
||||
const handleClientUrlChange = (value: string) => {
|
||||
setClientUrl(value);
|
||||
chrome.storage.local.set({ clientUrl: value });
|
||||
const handleCloseSettings = () => {
|
||||
chrome.storage.local.set({
|
||||
navigateSidepanel: 'sidepanel',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -71,17 +87,33 @@ const Settings = () => {
|
||||
<TextInput
|
||||
label="Client URL"
|
||||
value={clientUrl}
|
||||
onChange={handleClientUrlChange}
|
||||
onChange={setClientUrl}
|
||||
placeholder="My client URL"
|
||||
fullWidth
|
||||
/>
|
||||
<TextInput
|
||||
label="Server URL"
|
||||
value={serverBaseUrl}
|
||||
onChange={handleBaseUrlChange}
|
||||
onChange={setServerBaseUrl}
|
||||
placeholder="My server URL"
|
||||
fullWidth
|
||||
/>
|
||||
<MainButton
|
||||
title="Done"
|
||||
disabled={
|
||||
currentClientUrl === clientUrl &&
|
||||
currentServerUrl === serverBaseUrl
|
||||
}
|
||||
variant="primary"
|
||||
onClick={handleSettingsChange}
|
||||
fullWidth
|
||||
/>
|
||||
<MainButton
|
||||
title="Close"
|
||||
variant="secondary"
|
||||
onClick={handleCloseSettings}
|
||||
fullWidth
|
||||
/>
|
||||
</StyledActionContainer>
|
||||
</StyledContainer>
|
||||
</StyledWrapper>
|
||||
|
||||
@ -46,44 +46,103 @@ const Sidepanel = () => {
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
|
||||
const setIframeState = useCallback(async () => {
|
||||
const store = await chrome.storage.local.get();
|
||||
if (isDefined(store.isAuthenticated)) setIsAuthenticated(true);
|
||||
const { tab: activeTab } = await chrome.runtime.sendMessage({
|
||||
action: 'getActiveTab',
|
||||
});
|
||||
const store = await chrome.storage.local.get([
|
||||
'isAuthenticated',
|
||||
'sidepanelUrl',
|
||||
'clientUrl',
|
||||
'accessToken',
|
||||
'refreshToken',
|
||||
]);
|
||||
|
||||
if (
|
||||
isDefined(activeTab) &&
|
||||
isDefined(store[`sidepanelUrl_${activeTab.id}`])
|
||||
store.isAuthenticated === true &&
|
||||
isDefined(store.accessToken) &&
|
||||
isDefined(store.refreshToken) &&
|
||||
new Date(store.accessToken.expiresAt).getTime() >= Date.now()
|
||||
) {
|
||||
const url = store[`sidepanelUrl_${activeTab.id}`];
|
||||
setClientUrl(url);
|
||||
} else if (isDefined(store.clientUrl)) {
|
||||
setClientUrl(store.clientUrl);
|
||||
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(() => {
|
||||
const initState = async () => {
|
||||
const store = await chrome.storage.local.get();
|
||||
if (isDefined(store.isAuthenticated)) setIsAuthenticated(true);
|
||||
void setIframeState();
|
||||
};
|
||||
void initState();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
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(() => {
|
||||
void setIframeState();
|
||||
}, [setIframeState, clientUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
chrome.storage.local.onChanged.addListener((store) => {
|
||||
if (isDefined(store.isAuthenticated)) {
|
||||
if (store.isAuthenticated.newValue === true) {
|
||||
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]);
|
||||
|
||||
|
||||
@ -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 Sidepanel from '~/options/Sidepanel';
|
||||
import App from '~/options/App';
|
||||
|
||||
import '~/index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(
|
||||
<AppThemeProvider>
|
||||
<React.StrictMode>
|
||||
<Sidepanel />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
</AppThemeProvider>,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user