From 4fea48668120fb4cb12487a76656e80c96c1acbe Mon Sep 17 00:00:00 2001 From: Kudaligi Amoghavarsha Date: Mon, 19 Feb 2024 18:20:30 +0530 Subject: [PATCH] Two-step process for toggling CDP state (#494) * Add a toast message component in design-system. Add mechanism in place to reload all the tabs. Add mechanism to show the reload button in the devtools. * Add toast message in the popup. * Persist toastmessage on reload of extension * Add styling to toast message * Add a button to match the chrome://flags toast-message. * Add a button to the popup. * Fix the order of firing of events. * Reduce Padding. * Reduce vertical padding. * Fix the text and add toast message in popup. * Add fullstop to the popup messaging. * Shorten the devtools message. * Remove persistence of the toastMessage * use sessionStorage instead of localStorage from web api * Fix refresh of the cookies. * revert changes. * Fix component showing analyse this tab on newtab open. * Fix service worker popup connection. * Fix the messaging. * Fix value being set add new state for display. * Fix 0 cookies landing page on multitab debugging issue. * Fix padding and the design. * Add breakpoints for the button. Add breakpoints for text. * Fix the fontsize * Fix breakpoints * Fix text for button. * Fix message not showing up in popup. * Add comment to listener. Add default case to switch block. * ref: use absolute to spread toast message * Fix the toastmessage while scrolling. --------- Co-authored-by: Mayank Rana --- .../src/components/button/index.tsx | 105 ++++++++++---- .../design-system/src/components/index.ts | 1 + .../src/components/toastMessage/index.tsx | 64 +++++++++ packages/extension/src/serviceWorker/index.ts | 124 ++++++++++------- .../extension/src/utils/canProcessCookies.ts | 2 +- packages/extension/src/view/devtools/app.tsx | 37 ++++- .../settings/components/settingsContainer.tsx | 4 +- .../stateProviders/syncCookieStore/index.tsx | 16 ++- .../syncSettingsStore/index.tsx | 131 +++++++++++++++--- packages/extension/src/view/popup/app.tsx | 63 +++++++-- .../stateProviders/syncCookieStore/index.tsx | 129 ++++++++++++++--- tailwind.config.cjs | 15 ++ 12 files changed, 557 insertions(+), 134 deletions(-) create mode 100644 packages/design-system/src/components/toastMessage/index.tsx diff --git a/packages/design-system/src/components/button/index.tsx b/packages/design-system/src/components/button/index.tsx index 1fad6cb3e..41e5ce6dc 100644 --- a/packages/design-system/src/components/button/index.tsx +++ b/packages/design-system/src/components/button/index.tsx @@ -25,7 +25,7 @@ interface ButtonProps { onClick?: () => void; loading?: boolean; type?: 'button' | 'submit' | 'reset'; - variant?: 'primary' | 'secondary' | 'danger' | 'small'; + variant?: 'primary' | 'secondary' | 'danger' | 'small' | 'large'; extraClasses?: string; } const Button = ({ @@ -36,30 +36,85 @@ const Button = ({ variant = 'primary', extraClasses = '', }: ButtonProps) => { - return ( - - ); + switch (variant) { + case 'small': + return ( + + ); + case 'large': + return ( + + ); + case 'primary': + return ( + + ); + case 'secondary': + return ( + + ); + case 'danger': + return ( + + ); + default: + return <>; + } }; export default Button; diff --git a/packages/design-system/src/components/index.ts b/packages/design-system/src/components/index.ts index 8376c2a3f..9c089bdae 100644 --- a/packages/design-system/src/components/index.ts +++ b/packages/design-system/src/components/index.ts @@ -40,6 +40,7 @@ export * from './table'; export { default as SearchInput } from './searchInput'; export * from './sidebar'; export { default as InspectButton } from './inspectButton'; +export { default as ToastMessage } from './toastMessage'; export { default as CookiesLandingContainer, type CookiesLandingContainerProps, diff --git a/packages/design-system/src/components/toastMessage/index.tsx b/packages/design-system/src/components/toastMessage/index.tsx new file mode 100644 index 000000000..6ebfde176 --- /dev/null +++ b/packages/design-system/src/components/toastMessage/index.tsx @@ -0,0 +1,64 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React, { forwardRef } from 'react'; + +/** + * Internal dependencies. + */ +import Button from '../button'; + +interface ToastMessageProps { + text: string; + onClick: () => void; + additionalStyles?: string; + textAdditionalStyles?: string; + variant?: 'primary' | 'secondary' | 'danger' | 'small' | 'large'; + buttonText?: string; +} +const ToastMessage = forwardRef( + function ToastMessage( + { + text, + onClick, + additionalStyles = '', + textAdditionalStyles = '', + variant = 'large', + buttonText = 'Reload', + }: ToastMessageProps, + ref + ) { + return ( +
+
+
+ {text} +
+
+
+
+
+ ); + } +); + +export default ToastMessage; diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 5551c61ee..70e3f3356 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -35,6 +35,7 @@ import { ALLOWED_NUMBER_OF_TABS } from '../constants'; import SynchnorousCookieStore from '../store/synchnorousCookieStore'; import canProcessCookies from '../utils/canProcessCookies'; import { getTab } from '../utils/getTab'; +import reloadCurrentTab from '../utils/reloadCurrentTab'; let cookieDB: CookieDatabase | null = null; let syncCookieStore: SynchnorousCookieStore | undefined; @@ -233,6 +234,10 @@ chrome.tabs.onRemoved.addListener((tabId) => { syncCookieStore?.removeTabData(tabId); }); +/** + * Fires when a profile with extension is started. + * @see https://developer.chrome.com/docs/extensions/reference/api/runtime#event-onStartup + */ chrome.runtime.onStartup.addListener(async () => { const storage = await chrome.storage.sync.get(); @@ -254,6 +259,16 @@ chrome.runtime.onStartup.addListener(async () => { * @see https://developer.chrome.com/docs/extensions/reference/api/tabs#event-onUpdated */ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { + if (!tab.url) { + return; + } + + syncCookieStore?.updateUrl(tabId, tab.url); + + if (changeInfo.status === 'loading' && tab.url) { + syncCookieStore?.removeCookieData(tabId); + } + try { if (globalIsUsingCDP) { await chrome.debugger.attach({ tabId }, '1.3'); @@ -265,16 +280,6 @@ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { } catch (error) { //Fail silently } - - if (!tab.url) { - return; - } - - syncCookieStore?.updateUrl(tabId, tab.url); - - if (changeInfo.status === 'loading' && tab.url) { - syncCookieStore?.removeCookieData(tabId); - } }); /** @@ -496,10 +501,16 @@ const listenToNewTab = async (tabId?: number) => { ); } + tabToRead = newTabId; + syncCookieStore?.addTabData(Number(newTabId)); syncCookieStore?.updateDevToolsState(Number(newTabId), true); syncCookieStore?.updatePopUpState(Number(newTabId), true); + const currentTab = await getTab(Number(newTabId)); + + syncCookieStore?.updateUrl(Number(newTabId), currentTab?.url ?? ''); + return newTabId; }; @@ -513,7 +524,6 @@ chrome.runtime.onMessage.addListener(async (request) => { request?.type === 'DevTools::ServiceWorker::SET_TAB_TO_READ' || request?.type === 'Popup::ServiceWorker::SET_TAB_TO_READ' ) { - tabToRead = request?.payload?.tabId?.toString(); const newTab = await listenToNewTab(request?.payload?.tabId); // Can't use sendResponse as delay is too long. So using sendMessage instead. @@ -527,20 +537,6 @@ chrome.runtime.onMessage.addListener(async (request) => { await chrome.tabs.reload(Number(newTab), { bypassCache: true }); } - if ( - request.type === 'DevTools::ServiceWorker::CHANGE_CDP_SETTING' || - request.type === 'Popup::ServiceWorker::CHANGE_CDP_SETTING' - ) { - if (typeof request.payload?.isUsingCDP !== 'undefined') { - globalIsUsingCDP = request.payload?.isUsingCDP; - const storage = await chrome.storage.sync.get(); - await chrome.storage.sync.set({ - ...storage, - isUsingCDP: globalIsUsingCDP, - }); - } - } - if ( request?.type === 'DevTools::ServiceWorker::DEVTOOLS_STATE_OPEN' && request?.payload?.tabId @@ -627,6 +623,45 @@ chrome.runtime.onMessage.addListener(async (request) => { JSON.parse(request?.payload?.cookieData) ); } + + if ( + request?.type === 'DevTools::ServiceWorker::RELOAD_ALL_TABS' || + request?.type === 'Popup::ServiceWorker::RELOAD_ALL_TABS' + ) { + const sessionStorage = await chrome.storage.session.get(); + if (Object.keys(sessionStorage).includes('allowedNumberOfTabs')) { + tabMode = sessionStorage.allowedNumberOfTabs; + } + + if (Object.keys(sessionStorage).includes('isUsingCDP')) { + globalIsUsingCDP = sessionStorage.isUsingCDP; + } + + await chrome.storage.session.set({ + allowedNumberOfTabs: tabMode, + isUsingCDP: globalIsUsingCDP, + pendingReload: false, + }); + + await chrome.storage.sync.set({ + allowedNumberOfTabs: tabMode, + isUsingCDP: globalIsUsingCDP, + }); + + await chrome.runtime.sendMessage({ + type: 'ServiceWorker::DevTools::TABS_RELOADED', + }); + + const tabs = await chrome.tabs.query({}); + await Promise.all( + tabs.map(async (tab) => { + if (!tab.id) { + return; + } + await reloadCurrentTab(tab.id); + }) + ); + } }); /** @@ -692,7 +727,6 @@ chrome.storage.sync.onChanged.addListener( syncCookieStore?.removeTabData(tab.id); - chrome.tabs.reload(Number(tab?.id), { bypassCache: true }); return tab; }); } else { @@ -712,17 +746,14 @@ chrome.storage.sync.onChanged.addListener( }, }); - await Promise.all( - tabs.map(async (tab) => { - if (!tab?.id) { - return; - } - syncCookieStore?.addTabData(tab.id); - syncCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); - syncCookieStore?.updateDevToolsState(tab.id, true); - await chrome.tabs.reload(Number(tab?.id), { bypassCache: true }); - }) - ); + tabs.forEach((tab) => { + if (!tab?.id) { + return; + } + syncCookieStore?.addTabData(tab.id); + syncCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); + syncCookieStore?.updateDevToolsState(tab.id, true); + }); } } ); @@ -764,28 +795,21 @@ chrome.storage.sync.onChanged.addListener( try { await chrome.debugger.detach({ tabId: tab.id }); - syncCookieStore?.removeCookieData(tab.id); syncCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); } catch (error) { // eslint-disable-next-line no-console console.warn(error); - } finally { - await chrome.tabs.reload(tab.id, { bypassCache: true }); } }) ); } else { - await Promise.all( - tabs.map(async (tab) => { - if (!tab.id) { - return; - } + tabs.forEach((tab) => { + if (!tab.id) { + return; + } - syncCookieStore?.removeCookieData(tab.id); - syncCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); - await chrome.tabs.reload(tab.id, { bypassCache: true }); - }) - ); + syncCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); + }); } } ); diff --git a/packages/extension/src/utils/canProcessCookies.ts b/packages/extension/src/utils/canProcessCookies.ts index 9838ad9ae..069895651 100644 --- a/packages/extension/src/utils/canProcessCookies.ts +++ b/packages/extension/src/utils/canProcessCookies.ts @@ -33,7 +33,7 @@ export default function canProcessCookies( return false; } - const _isSingleTabProcessingMode = tabMode && tabMode !== 'unlimited'; + const _isSingleTabProcessingMode = tabMode !== 'unlimited'; if (_isSingleTabProcessingMode) { if (currentTabId?.toString() !== tabToRead) { diff --git a/packages/extension/src/view/devtools/app.tsx b/packages/extension/src/view/devtools/app.tsx index cc03ccf88..ba0568594 100644 --- a/packages/extension/src/view/devtools/app.tsx +++ b/packages/extension/src/view/devtools/app.tsx @@ -26,6 +26,7 @@ import { useSidebar, type SidebarItems, InspectButton, + ToastMessage, } from '@ps-analysis-tool/design-system'; import { UNKNOWN_FRAME_KEY, @@ -73,9 +74,15 @@ const App: React.FC = () => { frameHasCookies: state.frameHasCookies, })); - const { allowedNumberOfTabs } = useSettingsStore(({ state }) => ({ - allowedNumberOfTabs: state.allowedNumberOfTabs, - })); + const mainRef = useRef(null); + const toastMessageRef = useRef(null); + + const { allowedNumberOfTabs, settingsChanged, handleSettingsChange } = + useSettingsStore(({ state, actions }) => ({ + allowedNumberOfTabs: state.allowedNumberOfTabs, + settingsChanged: state.settingsChanged, + handleSettingsChange: actions.handleSettingsChange, + })); const listenToMouseChange = useCallback(() => { if (contextInvalidatedRef.current) { @@ -261,7 +268,7 @@ const App: React.FC = () => { )} {!contextInvalidated && ( -
+
{ visibleWidth={sidebarWidth} /> -
-
{activePanel}
+
{ + if (mainRef.current && toastMessageRef.current) { + toastMessageRef.current.style.bottom = + '-' + mainRef.current.scrollTop + 'px'; + } + }} + className="h-full flex-1 overflow-auto relative" + > +
{activePanel}
+ {settingsChanged && ( + + )}
)} diff --git a/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx b/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx index 4a7bbbbc7..c976969f4 100644 --- a/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx +++ b/packages/extension/src/view/devtools/components/settings/components/settingsContainer.tsx @@ -38,8 +38,8 @@ interface settingsToReturnObject { const SettingsContainer = () => { const { allowedNumberOfTabs, isUsingCDP, setIsUsingCDP, setProcessingMode } = useSettingsStore(({ state, actions }) => ({ - allowedNumberOfTabs: state.allowedNumberOfTabs, - isUsingCDP: state.isUsingCDP, + allowedNumberOfTabs: state.allowedNumberOfTabsForSettingsPageDisplay, + isUsingCDP: state.isUsingCDPForSettingsPageDisplay, setProcessingMode: actions.setProcessingMode, setIsUsingCDP: actions.setIsUsingCDP, })); diff --git a/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx b/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx index 8ce640083..895591e7f 100644 --- a/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx +++ b/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx @@ -123,9 +123,12 @@ export const Provider = ({ children }: PropsWithChildren) => { // This was converted to useRef because setting state was creating a race condition in rerendering the provider. const isCurrentTabBeingListenedToRef = useRef(false); - const { allowedNumberOfTabs } = useSettingsStore(({ state }) => ({ - allowedNumberOfTabs: state.allowedNumberOfTabs, - })); + const { allowedNumberOfTabs, setSettingsChanged } = useSettingsStore( + ({ state, actions }) => ({ + allowedNumberOfTabs: state.allowedNumberOfTabs, + setSettingsChanged: actions.setSettingsChanged, + }) + ); /** * Set tab frames state for frame ids and frame URLs from using chrome.webNavigation.getAllFrames @@ -297,6 +300,7 @@ export const Provider = ({ children }: PropsWithChildren) => { ); isCurrentTabBeingListenedToRef.current = tabId?.toString() === message?.payload?.tabId; + setTabToRead(message?.payload?.tabId?.toString() || null); setTabFrames(null); setLoading(false); setCanStartInspecting(false); @@ -333,6 +337,10 @@ export const Provider = ({ children }: PropsWithChildren) => { } } } + + if (message.type === 'ServiceWorker::DevTools::TABS_RELOADED') { + setSettingsChanged(false); + } }; chrome.runtime.onMessage.addListener(listener); @@ -340,7 +348,7 @@ export const Provider = ({ children }: PropsWithChildren) => { return () => { chrome.runtime.onMessage.removeListener(listener); }; - }, [getAllFramesForCurrentTab, tabId]); + }, [getAllFramesForCurrentTab, tabId, setSettingsChanged]); const tabUpdateListener = useCallback( async (_tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => { diff --git a/packages/extension/src/view/devtools/stateProviders/syncSettingsStore/index.tsx b/packages/extension/src/view/devtools/stateProviders/syncSettingsStore/index.tsx index e6b5d9726..2db0473a2 100644 --- a/packages/extension/src/view/devtools/stateProviders/syncSettingsStore/index.tsx +++ b/packages/extension/src/view/devtools/stateProviders/syncSettingsStore/index.tsx @@ -48,10 +48,15 @@ export interface SettingStoreContext { browserInformation: string | null; OSInformation: string | null; isUsingCDP: boolean; + settingsChanged: boolean; + allowedNumberOfTabsForSettingsPageDisplay: string | null; + isUsingCDPForSettingsPageDisplay: boolean; }; actions: { setProcessingMode: (newState: boolean) => void; setIsUsingCDP: (newValue: boolean) => void; + handleSettingsChange: () => void; + setSettingsChanged: React.Dispatch>; }; } @@ -63,10 +68,15 @@ const initialState: SettingStoreContext = { browserInformation: null, OSInformation: null, isUsingCDP: false, + settingsChanged: false, + allowedNumberOfTabsForSettingsPageDisplay: null, + isUsingCDPForSettingsPageDisplay: false, }, actions: { setIsUsingCDP: noop, setProcessingMode: noop, + handleSettingsChange: noop, + setSettingsChanged: noop, }, }; @@ -76,8 +86,18 @@ export const Provider = ({ children }: PropsWithChildren) => { const [allowedNumberOfTabs, setAllowedNumberOfTabs] = useState( null ); + const [ + allowedNumberOfTabsForSettingsPageDisplay, + setAllowedNumberOfTabsForSettingsPageDisplay, + ] = useState(null); + + const [settingsChanged, setSettingsChanged] = useState(false); const [isUsingCDP, setIsUsingCDP] = useState(false); + const [ + isUsingCDPForSettingsPageDisplay, + setIsUsingCDPForSettingsPageDisplay, + ] = useState(false); const [currentTabs, setCurrentTabs] = useState(0); @@ -90,13 +110,40 @@ export const Provider = ({ children }: PropsWithChildren) => { useState(null); const intitialSync = useCallback(async () => { + const sessionStorage = await chrome.storage.session.get(); const currentSettings = await chrome.storage.sync.get(); + if (Object.keys(sessionStorage).includes('pendingReload')) { + setSettingsChanged(sessionStorage?.pendingReload); + + if (Object.keys(sessionStorage).includes('allowedNumberOfTabs')) { + setAllowedNumberOfTabsForSettingsPageDisplay( + sessionStorage.allowedNumberOfTabs + ); + } else { + setAllowedNumberOfTabsForSettingsPageDisplay( + currentSettings.allowedNumberOfTabs + ); + } + + if (Object.keys(sessionStorage).includes('isUsingCDP')) { + setIsUsingCDPForSettingsPageDisplay(sessionStorage.isUsingCDP); + } else { + setIsUsingCDPForSettingsPageDisplay(currentSettings.isUsingCDP); + } + } else { + setAllowedNumberOfTabsForSettingsPageDisplay( + currentSettings.allowedNumberOfTabs + ); + setIsUsingCDPForSettingsPageDisplay(currentSettings.isUsingCDP); + } + if (Object.keys(currentSettings).includes('allowedNumberOfTabs')) { - setAllowedNumberOfTabs(currentSettings?.allowedNumberOfTabs); + setAllowedNumberOfTabs(currentSettings.allowedNumberOfTabs); } + if (Object.keys(currentSettings).includes('isUsingCDP')) { - setIsUsingCDP(currentSettings?.isUsingCDP); + setIsUsingCDP(currentSettings.isUsingCDP); } chrome.tabs.query({}, (tabs) => { @@ -133,51 +180,94 @@ export const Provider = ({ children }: PropsWithChildren) => { } }, [OSInformation]); - const setProcessingMode = useCallback(async (newState: boolean) => { + const setProcessingMode = useCallback((newState: boolean) => { const valueToBeSet: boolean | string = newState ? 'unlimited' : 'single'; - - const currentSettings = await chrome.storage.sync.get(); - - chrome.storage.sync.set({ - ...currentSettings, + setAllowedNumberOfTabsForSettingsPageDisplay(valueToBeSet); + setSettingsChanged(true); + chrome.storage.session.set({ allowedNumberOfTabs: valueToBeSet, + pendingReload: true, }); }, []); const _setUsingCDP = useCallback((newValue: boolean) => { - chrome.runtime.sendMessage({ - type: 'DevTools::ServiceWorker::CHANGE_CDP_SETTING', - payload: { - isUsingCDP: newValue, - }, + setIsUsingCDPForSettingsPageDisplay(newValue); + setSettingsChanged(true); + chrome.storage.session.set({ + isUsingCDP: newValue, + pendingReload: true, }); - setIsUsingCDP(newValue); }, []); const storeChangeListener = useCallback( (changes: { [key: string]: chrome.storage.StorageChange }) => { if ( - changes?.allowedNumberOfTabs && - changes?.allowedNumberOfTabs?.newValue + Object.keys(changes).includes('allowedNumberOfTabs') && + Object.keys(changes.allowedNumberOfTabs).includes('newValue') ) { setAllowedNumberOfTabs(changes?.allowedNumberOfTabs?.newValue); } - if (changes?.isUsingCDP) { + if ( + Object.keys(changes).includes('isUsingCDP') && + Object.keys(changes.isUsingCDP).includes('newValue') + ) { setIsUsingCDP(changes?.isUsingCDP?.newValue); } }, [] ); + const sessionStoreChangeListener = useCallback( + (changes: { [key: string]: chrome.storage.StorageChange }) => { + if ( + Object.keys(changes).includes('allowedNumberOfTabs') && + Object.keys(changes.allowedNumberOfTabs).includes('newValue') + ) { + setAllowedNumberOfTabsForSettingsPageDisplay( + changes.allowedNumberOfTabs.newValue + ); + } + + if ( + Object.keys(changes).includes('isUsingCDP') && + Object.keys(changes.isUsingCDP).includes('newValue') + ) { + setIsUsingCDPForSettingsPageDisplay(changes.isUsingCDP.newValue); + } + + if ( + Object.keys(changes).includes('pendingReload') && + Object.keys(changes.pendingReload).includes('newValue') + ) { + setSettingsChanged(changes.pendingReload.newValue); + } + }, + [] + ); + + const handleSettingsChange = useCallback(async () => { + if (settingsChanged) { + await chrome.runtime.sendMessage({ + type: 'DevTools::ServiceWorker::RELOAD_ALL_TABS', + }); + + setSettingsChanged(false); + } + }, [settingsChanged]); + useEffect(() => { intitialSync(); chrome.storage.sync.onChanged.addListener(storeChangeListener); + chrome.storage.session.onChanged.addListener(sessionStoreChangeListener); return () => { chrome.storage.sync.onChanged.removeListener(storeChangeListener); + chrome.storage.session.onChanged.removeListener( + sessionStoreChangeListener + ); }; - }, [intitialSync, storeChangeListener]); + }, [intitialSync, storeChangeListener, sessionStoreChangeListener]); return ( { browserInformation, OSInformation, isUsingCDP, + settingsChanged, + allowedNumberOfTabsForSettingsPageDisplay, + isUsingCDPForSettingsPageDisplay, }, actions: { setProcessingMode, setIsUsingCDP: _setUsingCDP, + handleSettingsChange, + setSettingsChanged, }, }} > diff --git a/packages/extension/src/view/popup/app.tsx b/packages/extension/src/view/popup/app.tsx index 837ff895d..f8e06df65 100644 --- a/packages/extension/src/view/popup/app.tsx +++ b/packages/extension/src/view/popup/app.tsx @@ -22,6 +22,7 @@ import { Button, CirclePieChart, ProgressBar, + ToastMessage, ToggleSwitch, prepareCookieStatsComponents, } from '@ps-analysis-tool/design-system'; @@ -44,6 +45,8 @@ const App: React.FC = () => { allowedNumberOfTabs, isUsingCDP, setIsUsingCDP, + settingsChanged, + handleSettingsChange, } = useCookieStore(({ state, actions }) => ({ cookieStats: state.tabCookieStats, isCurrentTabBeingListenedTo: state.isCurrentTabBeingListenedTo, @@ -51,8 +54,10 @@ const App: React.FC = () => { returningToSingleTab: state.returningToSingleTab, allowedNumberOfTabs: state.allowedNumberOfTabs, onChromeUrl: state.onChromeUrl, - isUsingCDP: state.isUsingCDP, + isUsingCDP: state.isUsingCDPForSettingsDisplay, setIsUsingCDP: actions.setIsUsingCDP, + handleSettingsChange: actions.handleSettingsChange, + settingsChanged: state.settingsChanged, changeListeningToThisTab: actions.changeListeningToThisTab, })); @@ -60,7 +65,7 @@ const App: React.FC = () => { if (onChromeUrl) { return ( - <> +
{

Its emptier than a cookie jar after a midnight snack! 🌌

- +
+ {settingsChanged && ( + + )} +
+
); } @@ -92,7 +107,7 @@ const App: React.FC = () => { allowedNumberOfTabs !== 'unlimited' ) { return ( - <> +
{ enabled={isUsingCDP} />
); } @@ -109,7 +134,7 @@ const App: React.FC = () => { (cookieStats?.firstParty.total === 0 && cookieStats?.thirdParty.total === 0) ) { return ( - <> +
{

Please try reloading the page

- +
+ {settingsChanged && ( + + )} +
+
); } const statsComponents = prepareCookieStatsComponents(cookieStats); return ( - <> +
{ {'Inspect cookies in the "Privacy Sandbox" panel of DevTools'}

- +
+ {settingsChanged && ( + + )} +
+
); }; diff --git a/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx b/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx index cef0c01ff..26189a98c 100644 --- a/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx +++ b/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx @@ -44,8 +44,12 @@ export interface CookieStoreContext { onChromeUrl: boolean; allowedNumberOfTabs: string | null; isUsingCDP: boolean; + settingsChanged: boolean; + allowedNumberOfTabsForSettingsDisplay: string | null; + isUsingCDPForSettingsDisplay: boolean; }; actions: { + handleSettingsChange: () => void; changeListeningToThisTab: () => void; setIsUsingCDP: (newValue: boolean) => void; }; @@ -80,8 +84,12 @@ const initialState: CookieStoreContext = { tabId: null, allowedNumberOfTabs: null, isUsingCDP: false, + settingsChanged: false, + allowedNumberOfTabsForSettingsDisplay: null, + isUsingCDPForSettingsDisplay: false, }, actions: { + handleSettingsChange: noop, changeListeningToThisTab: noop, setIsUsingCDP: noop, }, @@ -95,9 +103,17 @@ export const Provider = ({ children }: PropsWithChildren) => { const [allowedNumberOfTabs, setAllowedNumberOfTabs] = useState( null ); + const [ + allowedNumberOfTabsForSettingsDisplay, + setAllowedNumberOfTabsForSettingsDisplay, + ] = useState(null); + + const [settingsChanged, setSettingsChanged] = useState(false); const [tabToRead, setTabToRead] = useState(''); const [isUsingCDP, setIsUsingCDP] = useState(false); + const [isUsingCDPForSettingsDisplay, setIsUsingCDPForSettingsDisplay] = + useState(false); const [tabCookieStats, setTabCookieStats] = useState(null); @@ -131,27 +147,47 @@ export const Provider = ({ children }: PropsWithChildren) => { }, 100); const _setUsingCDP = useCallback((newValue: boolean) => { - chrome.runtime.sendMessage({ - type: 'Popup::ServiceWorker::CHANGE_CDP_SETTING', - payload: { - isUsingCDP: newValue, - }, + setIsUsingCDPForSettingsDisplay(newValue); + chrome.storage.session.set({ + isUsingCDP: newValue, + pendingReload: true, }); - setIsUsingCDP(newValue); }, []); const intitialSync = useCallback(async () => { - const tab = await getCurrentTab(); + const sessionStorage = await chrome.storage.session.get(); + const currentSettings = await chrome.storage.sync.get(); + + if (Object.keys(sessionStorage).includes('pendingReload')) { + setSettingsChanged(sessionStorage?.pendingReload); + + if (Object.keys(sessionStorage).includes('allowedNumberOfTabs')) { + setAllowedNumberOfTabsForSettingsDisplay( + sessionStorage.allowedNumberOfTabs + ); + } else { + setAllowedNumberOfTabsForSettingsDisplay( + currentSettings.allowedNumberOfTabs + ); + } - const extensionStorage = await chrome.storage.sync.get(); + if (Object.keys(sessionStorage).includes('isUsingCDP')) { + setIsUsingCDPForSettingsDisplay(sessionStorage.isUsingCDP); + } else { + setIsUsingCDPForSettingsDisplay(currentSettings.isUsingCDP); + } + } - if (Object.keys(extensionStorage).includes('allowedNumberOfTabs')) { - setAllowedNumberOfTabs(extensionStorage?.allowedNumberOfTabs); + if (Object.keys(currentSettings).includes('allowedNumberOfTabs')) { + setAllowedNumberOfTabs(currentSettings.allowedNumberOfTabs); } - if (Object.keys(extensionStorage).includes('isUsingCDP')) { - setIsUsingCDP(extensionStorage?.isUsingCDP); + + if (Object.keys(currentSettings).includes('isUsingCDP')) { + setIsUsingCDP(currentSettings.isUsingCDP); } + const tab = await getCurrentTab(); + const availableTabs = await chrome.tabs.query({}); if ( @@ -203,6 +239,36 @@ export const Provider = ({ children }: PropsWithChildren) => { }); }, [tabId]); + const sessionStoreChangeListener = useCallback( + (changes: { [key: string]: chrome.storage.StorageChange }) => { + if ( + Object.keys(changes).includes('allowedNumberOfTabs') && + Object.keys(changes.allowedNumberOfTabs).includes('newValue') + ) { + setAllowedNumberOfTabs(changes?.allowedNumberOfTabs?.newValue); + setSettingsChanged(true); + } + + if ( + Object.keys(changes).includes('isUsingCDP') && + Object.keys(changes.isUsingCDP).includes('newValue') + ) { + setIsUsingCDP(changes?.isUsingCDP?.newValue); + setSettingsChanged(true); + } + }, + [] + ); + + const handleSettingsChange = useCallback(async () => { + if (settingsChanged) { + await chrome.runtime.sendMessage({ + type: 'Popup::ServiceWorker::RELOAD_ALL_TABS', + }); + setSettingsChanged(false); + } + }, [settingsChanged]); + useEffect(() => { const listener = (message: { type: string; @@ -252,6 +318,10 @@ export const Provider = ({ children }: PropsWithChildren) => { } setLoading(false); } + + if (message.type === 'ServiceWorker::TABS_RELOADED') { + setSettingsChanged(false); + } }; chrome.runtime.onMessage.addListener(listener); @@ -261,12 +331,24 @@ export const Provider = ({ children }: PropsWithChildren) => { }; }, [setDebouncedStats, tabId, allowedNumberOfTabs]); - const changeSyncStorageListener = useCallback(async () => { - const extensionStorage = await chrome.storage.sync.get(); - if (extensionStorage?.allowedNumberOfTabs) { - setAllowedNumberOfTabs(extensionStorage?.allowedNumberOfTabs); - } - }, []); + const changeSyncStorageListener = useCallback( + (changes: { [key: string]: chrome.storage.StorageChange }) => { + if ( + Object.keys(changes).includes('allowedNumberOfTabs') && + Object.keys(changes.allowedNumberOfTabs).includes('newValue') + ) { + setAllowedNumberOfTabs(changes?.allowedNumberOfTabs?.newValue); + } + + if ( + Object.keys(changes).includes('isUsingCDP') && + Object.keys(changes.isUsingCDP).includes('newValue') + ) { + setIsUsingCDP(changes?.isUsingCDP?.newValue); + } + }, + [] + ); useEffect(() => { intitialSync(); @@ -274,10 +356,15 @@ export const Provider = ({ children }: PropsWithChildren) => { useEffect(() => { chrome.storage.sync.onChanged.addListener(changeSyncStorageListener); + chrome.storage.session.onChanged.addListener(sessionStoreChangeListener); + return () => { chrome.storage.sync.onChanged.removeListener(changeSyncStorageListener); + chrome.storage.session.onChanged.removeListener( + sessionStoreChangeListener + ); }; - }, [changeSyncStorageListener]); + }, [changeSyncStorageListener, sessionStoreChangeListener]); return ( { onChromeUrl, allowedNumberOfTabs, isUsingCDP, + settingsChanged, + allowedNumberOfTabsForSettingsDisplay, + isUsingCDPForSettingsDisplay, }, actions: { changeListeningToThisTab, setIsUsingCDP: _setUsingCDP, + handleSettingsChange, }, }} > diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 2dcdf2248..ceba489cf 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -38,6 +38,7 @@ module.exports = { boxShadow: { '3xl': '0px 38px 90px 0px rgba(0, 0, 0, 0.25), 0px 0px 2px 0px rgba(0, 0, 0, 0.05), 0px 0px 1px 0px rgba(0, 0, 0, 0.60)', + xxs: '0 -2px 2px 0 rgba(0, 0, 0, 0.1)', }, keyframes: { 'horizontal-spinner-keyframes': { @@ -48,6 +49,15 @@ module.exports = { animation: { 'horizontal-spinner': 'horizontal-spinner-keyframes 2s linear infinite', }, + screens: { + ...defaultTheme.screens, + xxs: '360px', + xs: '480px', + }, + }, + borderRadius: { + ...defaultTheme.borderRadius, + xs: '3px', }, fontFamily: { ...defaultTheme.fontFamily, @@ -56,6 +66,8 @@ module.exports = { ...defaultTheme.fontSize, xxxs: '0.625rem', // 10px - Only for edge cases xxl: '1.375rem', // 22px + xsm: '0.9375rem', + xxs: '0.8125rem', }, fontWeight: { ...defaultTheme.fontWeight, @@ -132,6 +144,9 @@ module.exports = { backgroundColor: { ...colors, primary: '#FFF', + 'google-blue': '#8AB4F8', + 'smurf-blue': '#1967D2', + beteleguese: '#4285F4', 'toggle-on': '#5CC971', 'flagged-row-odd-dark': '#5e5108', 'flagged-row-even-dark': '#796700',