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:
Aditya Pimpalkar
2024-05-30 11:58:45 +01:00
committed by GitHub
parent d770e56e31
commit a12c1aad5e
30 changed files with 511 additions and 231 deletions

View 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;

View File

@ -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>

View File

@ -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]);

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 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>,
);