From 11fa712e2685200d329319a3038a63b409f95360 Mon Sep 17 00:00:00 2001 From: Diogo Soares <32431609+DiogoSoaress@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:13:09 +0000 Subject: [PATCH 1/7] feat: Integrate beamer (#3474) * feat: Add whats new button in sidebar, include beamer script * Use src branch dependency and continue customizing beamer * style: notifications badge * Load Beamer from the CookieBanner preferences * update beamer config object on close * chore: Bump version on components package * chore: Add beamer type to global window type and remove ts-nocheck * fix: useRef for beamer script tag to remove * fix: Add Beamer params and methods type definitions * refactor: Pass generic to loadFromCookie * fix: pass value as Generic instead of the whole Record type * fix: PR feedback Co-authored-by: Usame Algan --- .env.example | 1 + package.json | 2 +- src/components/AppLayout/Sidebar/index.tsx | 56 +++++++++++++++++----- src/components/CookiesBanner/index.tsx | 14 ++++-- src/components/List/index.tsx | 24 +++++++++- src/logic/cookies/model/cookie.ts | 8 ++++ src/logic/cookies/utils/index.ts | 9 ++-- src/types/Beamer.d.ts | 50 +++++++++++++++++++ src/types/definitions.d.ts | 3 ++ src/utils/beamer.ts | 39 +++++++++++++++ src/utils/constants.ts | 1 + src/utils/googleAnalytics.ts | 4 +- src/utils/intercom.ts | 6 +-- yarn.lock | 8 ++-- 14 files changed, 193 insertions(+), 32 deletions(-) create mode 100644 src/types/Beamer.d.ts create mode 100644 src/utils/beamer.ts diff --git a/.env.example b/.env.example index 667de97615..948cfb6470 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,7 @@ REACT_APP_INFURA_TOKEN= REACT_APP_IPFS_GATEWAY=https://ipfs.io/ipfs REACT_APP_SENTRY_DSN= REACT_APP_SAFE_APPS_RPC_INFURA_TOKEN= +REACT_APP_BEAMER_PRODUCT_ID= # For production environments REACT_APP_INTERCOM_ID= diff --git a/package.json b/package.json index a2da67c944..7c752024f9 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2", "@gnosis.pm/safe-core-sdk": "^1.3.0", "@gnosis.pm/safe-deployments": "^1.8.0", - "@gnosis.pm/safe-react-components": "^0.9.0", + "@gnosis.pm/safe-react-components": "^0.9.8", "@gnosis.pm/safe-react-gateway-sdk": "2.8.3", "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.11.0", diff --git a/src/components/AppLayout/Sidebar/index.tsx b/src/components/AppLayout/Sidebar/index.tsx index f99269c858..1af9757c01 100644 --- a/src/components/AppLayout/Sidebar/index.tsx +++ b/src/components/AppLayout/Sidebar/index.tsx @@ -1,11 +1,16 @@ import { lazy, useMemo } from 'react' import styled from 'styled-components' -import { Divider, IconText } from '@gnosis.pm/safe-react-components' +import { Divider } from '@gnosis.pm/safe-react-components' +import { useDispatch } from 'react-redux' -import List, { ListItemType } from 'src/components/List' +import List, { ListItemType, StyledListItem, StyledListItemText } from 'src/components/List' import SafeHeader from './SafeHeader' import { IS_PRODUCTION } from 'src/utils/constants' import { wrapInSuspense } from 'src/utils/wrapInSuspense' +import ListIcon from 'src/components/List/ListIcon' +import { openCookieBanner } from 'src/logic/cookies/store/actions/openCookieBanner' +import { loadFromCookie } from 'src/logic/cookies/utils' +import { COOKIES_KEY, BannerCookiesType } from 'src/logic/cookies/model/cookie' const StyledDivider = styled(Divider)` margin: 16px -8px 0; @@ -15,18 +20,24 @@ const HelpContainer = styled.div` margin-top: auto; ` +const HelpList = styled.div` + margin: 24px 0; +` + const HelpCenterLink = styled.a` - height: 30px; - width: 166px; - padding: 6px 0 8px 16px; - margin: 14px 0px; + width: 100%; + display: flex; + position: relative; + box-sizing: border-box; + text-align: left; + align-items: center; + padding: 8px 16px; + justify-content: flex-start; text-decoration: none; - display: block; + border-radius: 8px; &:hover { - border-radius: 8px; background-color: ${({ theme }) => theme.colors.background}; - box-sizing: content-box; } p { font-family: ${({ theme }) => theme.fonts.fontFamily}; @@ -67,8 +78,21 @@ const Sidebar = ({ onReceiveClick, onNewTransactionClick, }: Props): React.ReactElement => { + const dispatch = useDispatch() const devTools = useMemo(() => lazyLoad('./DevTools'), []) const debugToggle = useMemo(() => lazyLoad('./DebugToggle'), []) + + const handleClick = async () => { + const cookiesState = await loadFromCookie(COOKIES_KEY) + if (!cookiesState) { + dispatch(openCookieBanner({ cookieBannerOpen: true })) + return + } + if (!cookiesState.acceptedIntercom) { + dispatch(openCookieBanner({ cookieBannerOpen: true, intercomAlertDisplayed: true })) + } + } + return ( <> - - - + + + + Whats new + + + + + Help Center + + ) diff --git a/src/components/CookiesBanner/index.tsx b/src/components/CookiesBanner/index.tsx index a0b1f1868a..bb5afa71d7 100644 --- a/src/components/CookiesBanner/index.tsx +++ b/src/components/CookiesBanner/index.tsx @@ -5,7 +5,7 @@ import { ReactElement, useEffect, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import Button from 'src/components/layout/Button' import Link from 'src/components/layout/Link' -import { COOKIES_KEY } from 'src/logic/cookies/model/cookie' +import { COOKIES_KEY, BannerCookiesType } from 'src/logic/cookies/model/cookie' import { openCookieBanner } from 'src/logic/cookies/store/actions/openCookieBanner' import { cookieBannerOpen } from 'src/logic/cookies/store/selectors' import { loadFromCookie, saveCookie } from 'src/logic/cookies/utils' @@ -16,6 +16,7 @@ import AlertRedIcon from './assets/alert-red.svg' import IntercomIcon from './assets/intercom.png' import { useSafeAppUrl } from 'src/logic/hooks/useSafeAppUrl' import { CookieAttributes } from 'js-cookie' +import { closeBeamer, loadBeamer } from 'src/utils/beamer' const isDesktop = process.env.REACT_APP_BUILD_FOR_DESKTOP @@ -107,6 +108,7 @@ const CookiesBanner = (): ReactElement => { const [localAnalytics, setLocalAnalytics] = useState(false) const [localIntercom, setLocalIntercom] = useState(false) const { getAppUrl } = useSafeAppUrl() + const beamerScriptRef = useRef() const showBanner = useSelector(cookieBannerOpen) const newAppUrl = getAppUrl() @@ -115,6 +117,7 @@ const CookiesBanner = (): ReactElement => { useEffect(() => { if (showIntercom && !isSafeAppView) { loadIntercom() + loadBeamer(beamerScriptRef) } }, [showIntercom, isSafeAppView]) @@ -126,7 +129,7 @@ const CookiesBanner = (): ReactElement => { useEffect(() => { async function fetchCookiesFromStorage() { - const cookiesState = await loadFromCookie(COOKIES_KEY) + const cookiesState = await loadFromCookie(COOKIES_KEY) if (!cookiesState) { dispatch.current(openCookieBanner({ cookieBannerOpen: true })) } else { @@ -140,7 +143,7 @@ const CookiesBanner = (): ReactElement => { const cookieConfig: CookieAttributes = { expires: acceptedAnalytics ? 365 : 7, } - await saveCookie(COOKIES_KEY, newState, cookieConfig) + await saveCookie(COOKIES_KEY, newState, cookieConfig) setLocalIntercom(newState.acceptedIntercom) setShowIntercom(newState.acceptedIntercom) } else { @@ -167,7 +170,7 @@ const CookiesBanner = (): ReactElement => { const cookieConfig: CookieAttributes = { expires: 365, } - await saveCookie(COOKIES_KEY, newState, cookieConfig) + await saveCookie(COOKIES_KEY, newState, cookieConfig) setShowAnalytics(!isDesktop) setShowIntercom(true) dispatch.current(openCookieBanner({ cookieBannerOpen: false })) @@ -182,7 +185,7 @@ const CookiesBanner = (): ReactElement => { const cookieConfig: CookieAttributes = { expires: localAnalytics ? 365 : 7, } - await saveCookie(COOKIES_KEY, newState, cookieConfig) + await saveCookie(COOKIES_KEY, newState, cookieConfig) setShowAnalytics(localAnalytics) setShowIntercom(localIntercom) @@ -192,6 +195,7 @@ const CookiesBanner = (): ReactElement => { if (!localIntercom && isIntercomLoaded()) { closeIntercom() + closeBeamer(beamerScriptRef) } dispatch.current(openCookieBanner({ cookieBannerOpen: false })) } diff --git a/src/components/List/index.tsx b/src/components/List/index.tsx index f3dda1a653..da8863a897 100644 --- a/src/components/List/index.tsx +++ b/src/components/List/index.tsx @@ -9,12 +9,17 @@ import ListItem, { ListItemProps } from '@material-ui/core/ListItem' import ListItemText from '@material-ui/core/ListItemText' import Collapse from '@material-ui/core/Collapse' import { FixedIcon } from '@gnosis.pm/safe-react-components' +import { secondary } from 'src/theme/variables' -const StyledListItem = styled(ListItem)` +export const StyledListItem = styled(ListItem)` &.MuiButtonBase-root.MuiListItem-root { margin: 4px 0; } + & .MuiListItemText-root span { + line-height: 1; + } + &.MuiListItem-button:hover { border-radius: 8px; } @@ -30,6 +35,17 @@ const StyledListItem = styled(ListItem)` fill: ${({ theme }) => theme.colors.primary}; } } + + & .beamer_icon.active { + background-color: ${secondary}; + top: auto; + bottom: 8px; + left: 31px; + width: 6px; + height: 6px; + border: white solid 1px; + text-indent: -9000px; + } ` const StyledListSubItem = styled(ListItem)` @@ -37,6 +53,10 @@ const StyledListSubItem = styled(ListItem)` margin: 4px 0; } + & .MuiListItemText-root span { + line-height: 1; + } + &.MuiListItem-button:hover { border-radius: 8px; } @@ -54,7 +74,7 @@ const StyledListSubItem = styled(ListItem)` } ` -const StyledListItemText = styled(ListItemText)` +export const StyledListItemText = styled(ListItemText)` span { font-family: ${({ theme }) => theme.fonts.fontFamily}; font-size: 0.76em; diff --git a/src/logic/cookies/model/cookie.ts b/src/logic/cookies/model/cookie.ts index e0b0179277..d7401d8a15 100644 --- a/src/logic/cookies/model/cookie.ts +++ b/src/logic/cookies/model/cookie.ts @@ -1,2 +1,10 @@ +export type BannerCookiesType = { + acceptedNecessary: boolean + acceptedIntercom: boolean + acceptedAnalytics: boolean +} +export type IntercomCookieType = { + userId: string +} export const COOKIES_KEY = 'COOKIES' export const COOKIES_KEY_INTERCOM = `${COOKIES_KEY}_INTERCOM` diff --git a/src/logic/cookies/utils/index.ts b/src/logic/cookies/utils/index.ts index 78dcc20825..cd5b18fed3 100644 --- a/src/logic/cookies/utils/index.ts +++ b/src/logic/cookies/utils/index.ts @@ -3,7 +3,10 @@ import { Errors, logError } from 'src/logic/exceptions/CodedException' const PREFIX = 'v1_MAINNET__' -export const loadFromCookie = async (key: string, withoutPrefix = false): Promise> => { +export const loadFromCookie = async >( + key: string, + withoutPrefix = false, +): Promise => { const prefix = withoutPrefix ? '' : PREFIX try { const stringifiedValue = await Cookies.get(`${prefix}${key}`) @@ -18,9 +21,9 @@ export const loadFromCookie = async (key: string, withoutPrefix = false): Promis } } -export const saveCookie = async ( +export const saveCookie = async >( key: string, - value: Record, + value: T, options: CookieAttributes, withoutPrefix = false, ): Promise => { diff --git a/src/types/Beamer.d.ts b/src/types/Beamer.d.ts new file mode 100644 index 0000000000..711a56c59e --- /dev/null +++ b/src/types/Beamer.d.ts @@ -0,0 +1,50 @@ +export interface BeamerConfig { + product_id: string + selector?: string + display?: 'left' | 'right' | 'popup' | 'in-app' + display_position?: string + top?: number + right?: number + bottom?: number + left?: number + embed?: boolean + button?: boolean + button_position?: 'top-left' | 'top-right' | 'bottom-right' | 'bottom-left' + icon?: 'bell_full' | 'bell_lines' | 'flame' | 'flame_alt' | 'alert_bubble' | 'alert_circle' | 'bullhorn' | 'thumbtack' + bounce?: boolean + notification_prompt?: 'sidebar' | 'popup' + notification_prompt_delay?: number + language?: string + filter?: string + force_filter?: string + filter_by_url?: boolean + mobile?: boolean + lazy?: boolean + alert?: boolean + force_button?: boolean + counter?: boolean + first_visit_unread?: number + standalone?: boolean + post_request?: boolean + delay?: number + callback?: (url: string, openInNewWindow: boolean) => boolean + onclick?: (url: string, openInNewWindow: boolean) => boolean + onopen?: () => boolean + onclose?: () => boolean + onerror?: () => unknown + user_firstname?: string + user_lastname?: string + user_email?: string + user_id?: string + multi_user?: boolean + nps_delay?: number + user_created_at?: number | string +} + +export interface BeamerMethods { + init: () => void + show: () => void + hide: () => void + update: (params: BeamerConfig) => void + destroy: () => void +} diff --git a/src/types/definitions.d.ts b/src/types/definitions.d.ts index 69e1e613d8..c48d7a7e40 100644 --- a/src/types/definitions.d.ts +++ b/src/types/definitions.d.ts @@ -1,5 +1,6 @@ import 'styled-components' import { theme } from '@gnosis.pm/safe-react-components' +import { BeamerConfig, BeamerMethods } from './Beamer.d' type Theme = typeof theme @@ -11,6 +12,8 @@ declare global { autoRefreshOnNetworkChange: boolean isMetaMask: boolean } + beamer_config?: BeamerConfig + Beamer?: BeamerMethods } } declare module '@openzeppelin/contracts/build/contracts/ERC721' diff --git a/src/utils/beamer.ts b/src/utils/beamer.ts new file mode 100644 index 0000000000..a20603975b --- /dev/null +++ b/src/utils/beamer.ts @@ -0,0 +1,39 @@ +import { BEAMER_ID } from './constants' +import { MutableRefObject } from 'react' +import { BeamerConfig } from 'src/types/Beamer' + +const BEAMER_URL = 'https://app.getbeamer.com/js/beamer-embed.js' + +const baseConfig: BeamerConfig = { + product_id: BEAMER_ID, +} + +export const loadBeamer = async (scriptRef: MutableRefObject): Promise => { + if (!BEAMER_ID) { + console.error('[Beamer] - In order to use Beamer you need to add an appID') + return + } + + window.beamer_config = { + ...baseConfig, + selector: 'whats-new-button', + display: 'left', + button: false, + bounce: false, + } + + scriptRef.current = document.createElement('script') + scriptRef.current.type = 'text/javascript' + scriptRef.current.defer = true + scriptRef.current.src = BEAMER_URL + const firstScript = document.getElementsByTagName('script')[0] + firstScript?.parentNode?.insertBefore(scriptRef.current, firstScript) + + scriptRef.current.addEventListener('load', () => window.Beamer?.init(), { once: true }) +} + +export const closeBeamer = (scriptRef: MutableRefObject): void => { + if (!window.Beamer || !scriptRef.current) return + window.Beamer.destroy() + scriptRef.current.remove() +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 774129fad0..fda213cc22 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -8,6 +8,7 @@ export const DEFAULT_CHAIN_ID = export const PUBLIC_URL = process.env.PUBLIC_URL export const TX_SERVICE_VERSION = '1' export const INTERCOM_ID = IS_PRODUCTION ? process.env.REACT_APP_INTERCOM_ID : 'plssl1fl' +export const BEAMER_ID = IS_PRODUCTION ? process.env.REACT_APP_BEAMER_ID || '' : 'kqHQMTfH40865' export const GOOGLE_ANALYTICS_ID = process.env.REACT_APP_GOOGLE_ANALYTICS || '' export const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || '' export const PORTIS_ID = process.env.REACT_APP_PORTIS_ID ?? '852b763d-f28b-4463-80cb-846d7ec5806b' diff --git a/src/utils/googleAnalytics.ts b/src/utils/googleAnalytics.ts index 16f260660b..4d2495e563 100644 --- a/src/utils/googleAnalytics.ts +++ b/src/utils/googleAnalytics.ts @@ -4,7 +4,7 @@ import { useSelector } from 'react-redux' import { getChainInfo, _getChainId } from 'src/config' import { currentChainId } from 'src/logic/config/store/selectors' -import { COOKIES_KEY } from 'src/logic/cookies/model/cookie' +import { COOKIES_KEY, BannerCookiesType } from 'src/logic/cookies/model/cookie' import { loadFromCookie, removeCookie } from 'src/logic/cookies/utils' import { GOOGLE_ANALYTICS_ID, IS_PRODUCTION } from './constants' import { capitalize } from './css' @@ -150,7 +150,7 @@ export const useAnalytics = (): UseAnalyticsResponse => { useEffect(() => { async function fetchCookiesFromStorage() { - const cookiesState = await loadFromCookie(COOKIES_KEY) + const cookiesState = await loadFromCookie(COOKIES_KEY) if (cookiesState) { const { acceptedAnalytics } = cookiesState setAnalyticsAllowed(acceptedAnalytics) diff --git a/src/utils/intercom.ts b/src/utils/intercom.ts index 957d88e71e..efe72ef90d 100644 --- a/src/utils/intercom.ts +++ b/src/utils/intercom.ts @@ -1,6 +1,6 @@ import crypto from 'crypto' import { CookieAttributes } from 'js-cookie' -import { COOKIES_KEY_INTERCOM } from 'src/logic/cookies/model/cookie' +import { COOKIES_KEY_INTERCOM, IntercomCookieType } from 'src/logic/cookies/model/cookie' import { loadFromCookie, saveCookie } from 'src/logic/cookies/utils' import { INTERCOM_ID } from 'src/utils/constants' @@ -9,14 +9,14 @@ let intercomLoaded = false export const isIntercomLoaded = (): boolean => intercomLoaded const getIntercomUserId = async () => { - const cookiesState = await loadFromCookie(COOKIES_KEY_INTERCOM) + const cookiesState = await loadFromCookie(COOKIES_KEY_INTERCOM) if (!cookiesState) { const userId = crypto.randomBytes(32).toString('hex') const newCookieState = { userId } const cookieConfig: CookieAttributes = { expires: 365, } - await saveCookie(COOKIES_KEY_INTERCOM, newCookieState, cookieConfig) + await saveCookie(COOKIES_KEY_INTERCOM, newCookieState, cookieConfig) return userId } const { userId } = cookiesState diff --git a/yarn.lock b/yarn.lock index 566794b1fa..35e1b68c9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1936,10 +1936,10 @@ resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-deployments/-/safe-deployments-1.8.0.tgz#856c15517274f924539ea4df40fffe6f009249a7" integrity sha512-xK2ZZXxCEGOw+6UZAeUmvqE/4C/XTpYmv1a8KzKUgSOxcGkHsIDqcjdKjqif7gOdnwHl4+XXJUtDQEuSLT4Scg== -"@gnosis.pm/safe-react-components@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-react-components/-/safe-react-components-0.9.0.tgz#c5169cab41cde96c9a375005f380cd37bd909fe0" - integrity sha512-+SAJEuK4Zf1eBCtMpe1uQlJBN73zttIQwevEch7PDUBhJysc/oyEHuhmhpZpMYryoFgVVGYz7FZtwv2NxmUsJQ== +"@gnosis.pm/safe-react-components@^0.9.8": + version "0.9.8" + resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-react-components/-/safe-react-components-0.9.8.tgz#b61f0641bbc00ada0c71428be824e69192110c17" + integrity sha512-yOq7QScr+s27GTEsmBZe7fOXd5ADe4Wy4wlpu0crpsgyDmiJWl3H/K+ynLyq+dwGM6aVIAgFcIId2dEnwzdQew== dependencies: react-media "^1.10.0" web3-utils "^1.6.0" From db20b66680635c3706d063485dd4fe41d863b714 Mon Sep 17 00:00:00 2001 From: Diogo Soares <32431609+DiogoSoaress@users.noreply.github.com> Date: Thu, 17 Feb 2022 10:14:29 +0000 Subject: [PATCH 2/7] feat: Cookies and privacy policy (#3517) * feat: link Beamer with the analytics cookies. Display different CookieBanner warning messages. * refactor: extract to an object cookie warning messages * refactor: clean up cookies reducer Co-authored-by: Aaron Cook * fix: update Beamer cookie alert copy * fix: handle Beamer with update cookies Co-authored-by: Aaron Cook --- src/components/AppLayout/Sidebar/index.tsx | 13 ++- src/components/CookiesBanner/index.tsx | 83 ++++++++++--------- src/logic/cookies/model/cookie.ts | 17 +++- .../cookies/store/actions/openCookieBanner.ts | 8 +- src/logic/cookies/store/reducer/cookies.ts | 27 ++++-- src/logic/cookies/store/selectors/index.ts | 5 +- src/store/index.ts | 4 +- 7 files changed, 97 insertions(+), 60 deletions(-) diff --git a/src/components/AppLayout/Sidebar/index.tsx b/src/components/AppLayout/Sidebar/index.tsx index 1af9757c01..75ad08b7f1 100644 --- a/src/components/AppLayout/Sidebar/index.tsx +++ b/src/components/AppLayout/Sidebar/index.tsx @@ -10,7 +10,7 @@ import { wrapInSuspense } from 'src/utils/wrapInSuspense' import ListIcon from 'src/components/List/ListIcon' import { openCookieBanner } from 'src/logic/cookies/store/actions/openCookieBanner' import { loadFromCookie } from 'src/logic/cookies/utils' -import { COOKIES_KEY, BannerCookiesType } from 'src/logic/cookies/model/cookie' +import { COOKIES_KEY, BannerCookiesType, COOKIE_IDS } from 'src/logic/cookies/model/cookie' const StyledDivider = styled(Divider)` margin: 16px -8px 0; @@ -88,8 +88,13 @@ const Sidebar = ({ dispatch(openCookieBanner({ cookieBannerOpen: true })) return } - if (!cookiesState.acceptedIntercom) { - dispatch(openCookieBanner({ cookieBannerOpen: true, intercomAlertDisplayed: true })) + if (!cookiesState.acceptedSupportAndUpdates) { + dispatch( + openCookieBanner({ + cookieBannerOpen: true, + key: COOKIE_IDS.BEAMER, + }), + ) } } @@ -124,7 +129,7 @@ const Sidebar = ({ - Whats new + What's new diff --git a/src/components/CookiesBanner/index.tsx b/src/components/CookiesBanner/index.tsx index bb5afa71d7..1ab461ac1e 100644 --- a/src/components/CookiesBanner/index.tsx +++ b/src/components/CookiesBanner/index.tsx @@ -5,9 +5,9 @@ import { ReactElement, useEffect, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import Button from 'src/components/layout/Button' import Link from 'src/components/layout/Link' -import { COOKIES_KEY, BannerCookiesType } from 'src/logic/cookies/model/cookie' -import { openCookieBanner } from 'src/logic/cookies/store/actions/openCookieBanner' -import { cookieBannerOpen } from 'src/logic/cookies/store/selectors' +import { COOKIES_KEY, BannerCookiesType, COOKIE_IDS, COOKIE_ALERTS } from 'src/logic/cookies/model/cookie' +import { closeCookieBanner, openCookieBanner } from 'src/logic/cookies/store/actions/openCookieBanner' +import { cookieBannerState } from 'src/logic/cookies/store/selectors' import { loadFromCookie, saveCookie } from 'src/logic/cookies/utils' import { mainFontFamily, md, primary, screenSm } from 'src/theme/variables' import { loadGoogleAnalytics, removeCookies } from 'src/utils/googleAnalytics' @@ -93,30 +93,29 @@ const useStyles = makeStyles({ }, } as any) -interface CookiesBannerFormProps { - alertMessage: boolean -} - const CookiesBanner = (): ReactElement => { const classes = useStyles() - const dispatch = useRef(useDispatch()) + const dispatch = useDispatch() const intercomLoaded = isIntercomLoaded() const [showAnalytics, setShowAnalytics] = useState(false) const [showIntercom, setShowIntercom] = useState(false) const [localNecessary, setLocalNecessary] = useState(true) const [localAnalytics, setLocalAnalytics] = useState(false) - const [localIntercom, setLocalIntercom] = useState(false) + const [localSupportAndUpdates, setLocalSupportAndUpdates] = useState(false) const { getAppUrl } = useSafeAppUrl() const beamerScriptRef = useRef() - const showBanner = useSelector(cookieBannerOpen) + const { key, cookieBannerOpen } = useSelector(cookieBannerState) const newAppUrl = getAppUrl() const isSafeAppView = newAppUrl !== null useEffect(() => { if (showIntercom && !isSafeAppView) { loadIntercom() + + // For use in non-webapps (Mobile and Desktop) + // https://www.getbeamer.com/help/how-to-install-beamer-using-our-api loadBeamer(beamerScriptRef) } }, [showIntercom, isSafeAppView]) @@ -131,24 +130,24 @@ const CookiesBanner = (): ReactElement => { async function fetchCookiesFromStorage() { const cookiesState = await loadFromCookie(COOKIES_KEY) if (!cookiesState) { - dispatch.current(openCookieBanner({ cookieBannerOpen: true })) + dispatch(openCookieBanner({ cookieBannerOpen: true })) } else { - const { acceptedIntercom, acceptedAnalytics, acceptedNecessary } = cookiesState - if (acceptedIntercom === undefined) { + const { acceptedSupportAndUpdates, acceptedAnalytics, acceptedNecessary } = cookiesState + if (acceptedSupportAndUpdates === undefined) { const newState = { acceptedNecessary, acceptedAnalytics, - acceptedIntercom: acceptedAnalytics, + acceptedSupportAndUpdates: acceptedAnalytics, } const cookieConfig: CookieAttributes = { expires: acceptedAnalytics ? 365 : 7, } await saveCookie(COOKIES_KEY, newState, cookieConfig) - setLocalIntercom(newState.acceptedIntercom) - setShowIntercom(newState.acceptedIntercom) + setLocalSupportAndUpdates(newState.acceptedSupportAndUpdates) + setShowIntercom(newState.acceptedSupportAndUpdates) } else { - setLocalIntercom(acceptedIntercom) - setShowIntercom(acceptedIntercom) + setLocalSupportAndUpdates(acceptedSupportAndUpdates) + setShowIntercom(acceptedSupportAndUpdates) } setLocalAnalytics(acceptedAnalytics) setLocalNecessary(acceptedNecessary) @@ -159,13 +158,13 @@ const CookiesBanner = (): ReactElement => { } } fetchCookiesFromStorage() - }, [showAnalytics, showIntercom]) + }, [dispatch, showAnalytics, showIntercom]) const acceptCookiesHandler = async () => { const newState = { acceptedNecessary: true, acceptedAnalytics: !isDesktop, - acceptedIntercom: true, + acceptedSupportAndUpdates: true, } const cookieConfig: CookieAttributes = { expires: 365, @@ -173,42 +172,43 @@ const CookiesBanner = (): ReactElement => { await saveCookie(COOKIES_KEY, newState, cookieConfig) setShowAnalytics(!isDesktop) setShowIntercom(true) - dispatch.current(openCookieBanner({ cookieBannerOpen: false })) + dispatch(closeCookieBanner()) } const closeCookiesBannerHandler = async () => { const newState = { acceptedNecessary: true, acceptedAnalytics: localAnalytics, - acceptedIntercom: localIntercom, + acceptedSupportAndUpdates: localSupportAndUpdates, } const cookieConfig: CookieAttributes = { expires: localAnalytics ? 365 : 7, } await saveCookie(COOKIES_KEY, newState, cookieConfig) setShowAnalytics(localAnalytics) - setShowIntercom(localIntercom) + setShowIntercom(localSupportAndUpdates) if (!localAnalytics) { removeCookies() } - if (!localIntercom && isIntercomLoaded()) { - closeIntercom() + if (!localSupportAndUpdates) { closeBeamer(beamerScriptRef) + if (isIntercomLoaded()) { + closeIntercom() + } } - dispatch.current(openCookieBanner({ cookieBannerOpen: false })) + dispatch(closeCookieBanner()) } - const CookiesBannerForm = (props: CookiesBannerFormProps) => { - const { alertMessage } = props + const CookiesBannerForm = () => { return (
- {alertMessage && ( + {key && (
- You attempted to open the customer support chat. Please accept the customer support cookie. + {COOKIE_ALERTS[key]}
)}

@@ -234,11 +234,11 @@ const CookiesBanner = (): ReactElement => {

} - label="Customer support" - name="Customer support" - onChange={() => setLocalIntercom((prev) => !prev)} - value={localIntercom} + control={} + label="Community support & updates" + name="Community support & updates" + onChange={() => setLocalSupportAndUpdates((prev) => !prev)} + value={localSupportAndUpdates} />
@@ -284,12 +284,17 @@ const CookiesBanner = (): ReactElement => { dispatch.current(openCookieBanner({ cookieBannerOpen: true, intercomAlertDisplayed: true }))} + onClick={() => + dispatch( + openCookieBanner({ + cookieBannerOpen: true, + key: COOKIE_IDS.INTERCOM, + }), + ) + } /> )} - {!isDesktop && showBanner?.cookieBannerOpen && ( - - )} + {!isDesktop && cookieBannerOpen && } ) } diff --git a/src/logic/cookies/model/cookie.ts b/src/logic/cookies/model/cookie.ts index d7401d8a15..cad9e2fbe3 100644 --- a/src/logic/cookies/model/cookie.ts +++ b/src/logic/cookies/model/cookie.ts @@ -1,10 +1,21 @@ +export const COOKIES_KEY = 'COOKIES' +export const COOKIES_KEY_INTERCOM = `${COOKIES_KEY}_INTERCOM` + +export enum COOKIE_IDS { + INTERCOM = 'INTERCOM', + BEAMER = 'BEAMER', +} + +export const COOKIE_ALERTS: Record = { + INTERCOM: 'You attempted to open the customer support chat. Please accept the community support & updates cookies', + BEAMER: "You attempted to open the What's New section. Please accept the community support & updates cookies.", +} + export type BannerCookiesType = { acceptedNecessary: boolean - acceptedIntercom: boolean + acceptedSupportAndUpdates: boolean acceptedAnalytics: boolean } export type IntercomCookieType = { userId: string } -export const COOKIES_KEY = 'COOKIES' -export const COOKIES_KEY_INTERCOM = `${COOKIES_KEY}_INTERCOM` diff --git a/src/logic/cookies/store/actions/openCookieBanner.ts b/src/logic/cookies/store/actions/openCookieBanner.ts index e3db9cb702..b7e3392857 100644 --- a/src/logic/cookies/store/actions/openCookieBanner.ts +++ b/src/logic/cookies/store/actions/openCookieBanner.ts @@ -2,6 +2,10 @@ import { createAction } from 'redux-actions' import { OpenCookieBannerPayload } from 'src/logic/cookies/store/reducer/cookies' -export const OPEN_COOKIE_BANNER = 'OPEN_COOKIE_BANNER' +export enum COOKIE_ACTIONS { + OPEN_BANNER = 'cookies/openCookieBanner', + CLOSE_BANNER = 'cookies/closeCookieBanner', +} -export const openCookieBanner = createAction(OPEN_COOKIE_BANNER) +export const openCookieBanner = createAction(COOKIE_ACTIONS.OPEN_BANNER) +export const closeCookieBanner = createAction(COOKIE_ACTIONS.CLOSE_BANNER) diff --git a/src/logic/cookies/store/reducer/cookies.ts b/src/logic/cookies/store/reducer/cookies.ts index d2a51b1fe9..d1cb0f96ea 100644 --- a/src/logic/cookies/store/reducer/cookies.ts +++ b/src/logic/cookies/store/reducer/cookies.ts @@ -1,21 +1,32 @@ -import { Map } from 'immutable' import { handleActions } from 'redux-actions' -import { OPEN_COOKIE_BANNER } from 'src/logic/cookies/store/actions/openCookieBanner' + +import { COOKIE_IDS } from 'src/logic/cookies/model/cookie' +import { COOKIE_ACTIONS } from 'src/logic/cookies/store/actions/openCookieBanner' export const COOKIES_REDUCER_ID = 'cookies' -type CookieState = Map +export type CookieState = { + cookieBannerOpen: boolean + key?: COOKIE_IDS +} + +const initialState: CookieState = { + cookieBannerOpen: false, + key: undefined, +} -export type OpenCookieBannerPayload = { cookieBannerOpen: boolean; intercomAlertDisplayed?: boolean } +export type OpenCookieBannerPayload = CookieState const cookiesReducer = handleActions( { - [OPEN_COOKIE_BANNER]: (state, action) => { - const { intercomAlertDisplayed = false, cookieBannerOpen } = action.payload - return state.set('cookieBannerOpen', { intercomAlertDisplayed, cookieBannerOpen }) + [COOKIE_ACTIONS.OPEN_BANNER]: (_, action) => { + return action.payload + }, + [COOKIE_ACTIONS.CLOSE_BANNER]: () => { + return initialState }, }, - Map(), + initialState, ) export default cookiesReducer diff --git a/src/logic/cookies/store/selectors/index.ts b/src/logic/cookies/store/selectors/index.ts index 9104be6934..599d4c7501 100644 --- a/src/logic/cookies/store/selectors/index.ts +++ b/src/logic/cookies/store/selectors/index.ts @@ -1,3 +1,4 @@ -import { COOKIES_REDUCER_ID } from 'src/logic/cookies/store/reducer/cookies' +import { CookieState, COOKIES_REDUCER_ID } from 'src/logic/cookies/store/reducer/cookies' +import { AppReduxState } from 'src/store' -export const cookieBannerOpen = (state) => state[COOKIES_REDUCER_ID].get('cookieBannerOpen') +export const cookieBannerState = (state: AppReduxState): CookieState => state[COOKIES_REDUCER_ID] diff --git a/src/store/index.ts b/src/store/index.ts index bb6fb62d07..4cdd7085d4 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -10,7 +10,7 @@ import { nftAssetReducer, nftTokensReducer, } from 'src/logic/collectibles/store/reducer/collectibles' -import cookiesReducer, { COOKIES_REDUCER_ID } from 'src/logic/cookies/store/reducer/cookies' +import cookiesReducer, { CookieState, COOKIES_REDUCER_ID } from 'src/logic/cookies/store/reducer/cookies' import currentSessionReducer, { CurrentSessionState, CURRENT_SESSION_REDUCER_ID, @@ -112,7 +112,7 @@ export type AppReduxState = CombinedState<{ [PENDING_TRANSACTIONS_ID]: PendingTransactionsState [NOTIFICATIONS_REDUCER_ID]: Map [CURRENCY_REDUCER_ID]: CurrencyValuesState - [COOKIES_REDUCER_ID]: Map + [COOKIES_REDUCER_ID]: CookieState [ADDRESS_BOOK_REDUCER_ID]: AddressBookState [CURRENT_SESSION_REDUCER_ID]: CurrentSessionState [CONFIG_REDUCER_ID]: ConfigState From e38ef2f32ef575bee0f562a630f86fe29d0b3412 Mon Sep 17 00:00:00 2001 From: Usame Algan <5880855+usame-algan@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:21:43 +0100 Subject: [PATCH 3/7] style: Overwrite inline beamer badge color (#3521) --- .env.example | 2 +- src/components/List/index.tsx | 2 +- src/utils/constants.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index b8a3c1f4d7..d4d5a82b0a 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,7 @@ REACT_APP_INFURA_TOKEN= REACT_APP_IPFS_GATEWAY=https://ipfs.io/ipfs REACT_APP_SENTRY_DSN= REACT_APP_SAFE_APPS_RPC_INFURA_TOKEN= -REACT_APP_BEAMER_PRODUCT_ID= +REACT_APP_BEAMER_ID= # For production environments REACT_APP_INTERCOM_ID= diff --git a/src/components/List/index.tsx b/src/components/List/index.tsx index da8863a897..e2b9b75116 100644 --- a/src/components/List/index.tsx +++ b/src/components/List/index.tsx @@ -37,7 +37,7 @@ export const StyledListItem = styled(ListItem)` } & .beamer_icon.active { - background-color: ${secondary}; + background-color: ${secondary} !important; top: auto; bottom: 8px; left: 31px; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index fda213cc22..80514adab3 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -8,7 +8,7 @@ export const DEFAULT_CHAIN_ID = export const PUBLIC_URL = process.env.PUBLIC_URL export const TX_SERVICE_VERSION = '1' export const INTERCOM_ID = IS_PRODUCTION ? process.env.REACT_APP_INTERCOM_ID : 'plssl1fl' -export const BEAMER_ID = IS_PRODUCTION ? process.env.REACT_APP_BEAMER_ID || '' : 'kqHQMTfH40865' +export const BEAMER_ID = IS_PRODUCTION ? process.env.REACT_APP_BEAMER_ID || 'ehlRMhQi41258' : 'kqHQMTfH40865' export const GOOGLE_ANALYTICS_ID = process.env.REACT_APP_GOOGLE_ANALYTICS || '' export const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || '' export const PORTIS_ID = process.env.REACT_APP_PORTIS_ID ?? '852b763d-f28b-4463-80cb-846d7ec5806b' From 85511d53312f4d3b242600cc8fbfc7ed4a1c424a Mon Sep 17 00:00:00 2001 From: Diogo Soares Date: Thu, 17 Feb 2022 11:18:25 +0000 Subject: [PATCH 4/7] chore: change the fallback BEAMER_ID for QA tests --- src/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 80514adab3..799a069902 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -8,7 +8,7 @@ export const DEFAULT_CHAIN_ID = export const PUBLIC_URL = process.env.PUBLIC_URL export const TX_SERVICE_VERSION = '1' export const INTERCOM_ID = IS_PRODUCTION ? process.env.REACT_APP_INTERCOM_ID : 'plssl1fl' -export const BEAMER_ID = IS_PRODUCTION ? process.env.REACT_APP_BEAMER_ID || 'ehlRMhQi41258' : 'kqHQMTfH40865' +export const BEAMER_ID = IS_PRODUCTION ? process.env.REACT_APP_BEAMER_ID || 'ehlRMhQi41258' : 'ehlRMhQi41258' export const GOOGLE_ANALYTICS_ID = process.env.REACT_APP_GOOGLE_ANALYTICS || '' export const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || '' export const PORTIS_ID = process.env.REACT_APP_PORTIS_ID ?? '852b763d-f28b-4463-80cb-846d7ec5806b' From 26484ee51230de5f6023b85c79959cf657b5094a Mon Sep 17 00:00:00 2001 From: Usame Algan <5880855+usame-algan@users.noreply.github.com> Date: Thu, 3 Mar 2022 10:06:16 +0100 Subject: [PATCH 5/7] fix: Remove Beamer cookies on consent withdraw (#3610) * fix: Remove Beamer cookies on consent withdraw, extract removeCookies function * refactor: Rename domain variable --- src/components/CookiesBanner/index.tsx | 9 +++++---- src/logic/cookies/utils/index.ts | 11 +++++++++++ src/utils/beamer.ts | 9 +++++++++ src/utils/googleAnalytics.ts | 11 ++--------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/components/CookiesBanner/index.tsx b/src/components/CookiesBanner/index.tsx index 1ab461ac1e..9458361284 100644 --- a/src/components/CookiesBanner/index.tsx +++ b/src/components/CookiesBanner/index.tsx @@ -8,15 +8,15 @@ import Link from 'src/components/layout/Link' import { COOKIES_KEY, BannerCookiesType, COOKIE_IDS, COOKIE_ALERTS } from 'src/logic/cookies/model/cookie' import { closeCookieBanner, openCookieBanner } from 'src/logic/cookies/store/actions/openCookieBanner' import { cookieBannerState } from 'src/logic/cookies/store/selectors' -import { loadFromCookie, saveCookie } from 'src/logic/cookies/utils' +import { loadFromCookie, removeCookies, saveCookie } from 'src/logic/cookies/utils' import { mainFontFamily, md, primary, screenSm } from 'src/theme/variables' -import { loadGoogleAnalytics, removeCookies } from 'src/utils/googleAnalytics' +import { GA_COOKIE_LIST, loadGoogleAnalytics } from 'src/utils/googleAnalytics' import { closeIntercom, isIntercomLoaded, loadIntercom } from 'src/utils/intercom' import AlertRedIcon from './assets/alert-red.svg' import IntercomIcon from './assets/intercom.png' import { useSafeAppUrl } from 'src/logic/hooks/useSafeAppUrl' import { CookieAttributes } from 'js-cookie' -import { closeBeamer, loadBeamer } from 'src/utils/beamer' +import { BEAMER_COOKIE_LIST, closeBeamer, loadBeamer } from 'src/utils/beamer' const isDesktop = process.env.REACT_APP_BUILD_FOR_DESKTOP @@ -189,11 +189,12 @@ const CookiesBanner = (): ReactElement => { setShowIntercom(localSupportAndUpdates) if (!localAnalytics) { - removeCookies() + removeCookies(GA_COOKIE_LIST) } if (!localSupportAndUpdates) { closeBeamer(beamerScriptRef) + removeCookies(BEAMER_COOKIE_LIST) if (isIntercomLoaded()) { closeIntercom() } diff --git a/src/logic/cookies/utils/index.ts b/src/logic/cookies/utils/index.ts index cd5b18fed3..c6967e2e71 100644 --- a/src/logic/cookies/utils/index.ts +++ b/src/logic/cookies/utils/index.ts @@ -1,6 +1,11 @@ import Cookies, { CookieAttributes } from 'js-cookie' import { Errors, logError } from 'src/logic/exceptions/CodedException' +export type Cookie = { + name: string + path: string +} + const PREFIX = 'v1_MAINNET__' export const loadFromCookie = async >( @@ -37,3 +42,9 @@ export const saveCookie = async >( } export const removeCookie = (key: string, path: string, domain: string): void => Cookies.remove(key, { path, domain }) + +export const removeCookies = (cookieList: Cookie[]): void => { + // Extracts domain, e.g. gnosis-safe.io + const domain = location.host.split('.').slice(-2).join('.') + cookieList.forEach((cookie) => removeCookie(cookie.name, cookie.path, `.${domain}`)) +} diff --git a/src/utils/beamer.ts b/src/utils/beamer.ts index a20603975b..090df307a0 100644 --- a/src/utils/beamer.ts +++ b/src/utils/beamer.ts @@ -1,6 +1,15 @@ import { BEAMER_ID } from './constants' import { MutableRefObject } from 'react' import { BeamerConfig } from 'src/types/Beamer' +import { Cookie } from 'src/logic/cookies/utils' + +export const BEAMER_COOKIE_LIST: Cookie[] = [ + { name: `_BEAMER_LAST_POST_SHOWN_${BEAMER_ID}`, path: '/' }, + { name: `_BEAMER_DATE_${BEAMER_ID}`, path: '/' }, + { name: `_BEAMER_FIRST_VISIT_${BEAMER_ID}`, path: '/' }, + { name: `_BEAMER_USER_ID_${BEAMER_ID}`, path: '/' }, + { name: `_BEAMER_FILTER_BY_URL_${BEAMER_ID}`, path: '/' }, +] const BEAMER_URL = 'https://app.getbeamer.com/js/beamer-embed.js' diff --git a/src/utils/googleAnalytics.ts b/src/utils/googleAnalytics.ts index 4d2495e563..2e93878422 100644 --- a/src/utils/googleAnalytics.ts +++ b/src/utils/googleAnalytics.ts @@ -5,7 +5,7 @@ import { getChainInfo, _getChainId } from 'src/config' import { currentChainId } from 'src/logic/config/store/selectors' import { COOKIES_KEY, BannerCookiesType } from 'src/logic/cookies/model/cookie' -import { loadFromCookie, removeCookie } from 'src/logic/cookies/utils' +import { Cookie, loadFromCookie } from 'src/logic/cookies/utils' import { GOOGLE_ANALYTICS_ID, IS_PRODUCTION } from './constants' import { capitalize } from './css' @@ -73,7 +73,7 @@ export const SETTINGS_EVENTS: Record = { OWNERS: { ...SAFE_EVENTS.SETTINGS, label: 'Owners' }, } -export const COOKIES_LIST = [ +export const GA_COOKIE_LIST: Cookie[] = [ { name: '_ga', path: '/' }, { name: '_gat', path: '/' }, { name: '_gid', path: '/' }, @@ -179,10 +179,3 @@ export const useAnalytics = (): UseAnalyticsResponse => { return { trackPage, trackEvent } } - -// we remove GA cookies manually as react-ga does not provides a utility for it. -export const removeCookies = (): void => { - // Extracts the main domain, e.g. gnosis-safe.io - const subDomain = location.host.split('.').slice(-2).join('.') - COOKIES_LIST.forEach((cookie) => removeCookie(cookie.name, cookie.path, `.${subDomain}`)) -} From 27929f3e92ecff7236f0bccfea0bfd974d061cab Mon Sep 17 00:00:00 2001 From: Usame Algan Date: Thu, 3 Mar 2022 10:37:47 +0100 Subject: [PATCH 6/7] fix: Remove additional beamer cookie on consent withdraw --- src/utils/beamer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/beamer.ts b/src/utils/beamer.ts index 090df307a0..125b7e7837 100644 --- a/src/utils/beamer.ts +++ b/src/utils/beamer.ts @@ -9,6 +9,7 @@ export const BEAMER_COOKIE_LIST: Cookie[] = [ { name: `_BEAMER_FIRST_VISIT_${BEAMER_ID}`, path: '/' }, { name: `_BEAMER_USER_ID_${BEAMER_ID}`, path: '/' }, { name: `_BEAMER_FILTER_BY_URL_${BEAMER_ID}`, path: '/' }, + { name: `_BEAMER_LAST_UPDATE_${BEAMER_ID}`, path: '/' }, ] const BEAMER_URL = 'https://app.getbeamer.com/js/beamer-embed.js' From b80d1964465c7b348ddbfdae929e54dca4f8ccbf Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Thu, 3 Mar 2022 15:48:03 +0100 Subject: [PATCH 7/7] Fix: remove Beamer LS + refactor CookieBanner (#3615) * Refactor CookieBanner * Hide What's New in desktop app * Remove unrelated test from footer tests --- .../AppLayout/Footer/Footer.test.tsx | 18 - src/components/AppLayout/Sidebar/index.tsx | 16 +- src/components/CookiesBanner/index.tsx | 409 +++++++++--------- src/logic/cookies/utils/index.ts | 13 +- .../Apps/components/AppsList.test.tsx | 4 +- .../Apps/hooks/appList/useCustomSafeApps.ts | 6 +- .../Apps/hooks/appList/usePinnedSafeApps.ts | 4 +- src/utils/beamer.ts | 56 ++- src/utils/googleAnalytics.ts | 19 +- src/utils/intercom.ts | 10 +- src/utils/storage/Storage.ts | 6 + 11 files changed, 286 insertions(+), 275 deletions(-) diff --git a/src/components/AppLayout/Footer/Footer.test.tsx b/src/components/AppLayout/Footer/Footer.test.tsx index 8652e14287..e93f7323d0 100644 --- a/src/components/AppLayout/Footer/Footer.test.tsx +++ b/src/components/AppLayout/Footer/Footer.test.tsx @@ -1,5 +1,4 @@ import Footer from './index' -import CookiesBanner from 'src/components/CookiesBanner' import { render, fireEvent, screen } from 'src/utils/test-utils' describe('