From be83d1e439074f7ee8fa25250aa037030a39afd7 Mon Sep 17 00:00:00 2001 From: Florian Bouron Date: Mon, 16 Oct 2023 19:46:07 +0200 Subject: [PATCH 1/6] POC Citrouille --- packages/extension/src/index.tsx | 117 ++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 16 deletions(-) diff --git a/packages/extension/src/index.tsx b/packages/extension/src/index.tsx index b43e11040..8b192a6ff 100644 --- a/packages/extension/src/index.tsx +++ b/packages/extension/src/index.tsx @@ -27,29 +27,114 @@ import { import reportWebVitals from './reportWebVitals'; import './index.css'; -const theme = createTheme({ +const halloweenTheme = createTheme({ palette: { - mode: 'dark' + primary: { + main: '#FF7518' // Vibrant Halloween orange + }, + secondary: { + main: '#FFA500' // Standard orange + }, + text: { + primary: '#FFFFFF', // White text + secondary: '#FFD700' // Gold text + }, + background: { + default: '#121212', // Dark background + paper: '#1E1E1E' // Slightly lighter background for paper elements + } }, - components: { - MuiBottomNavigation: { - styleOverrides: { - root: { - backgroundColor: 'transparent', - borderTop: 'solid 1px #bcbcbc' - } - } + shape: { + borderRadius: 8 // Adjust as needed + }, + typography: { + fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', + fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightBold: 700, + h1: { + fontSize: '2.5rem', + fontWeight: 700 + }, + h2: { + fontSize: '2rem', + fontWeight: 700 + }, + h3: { + fontSize: '1.8rem', + fontWeight: 700 + }, + h4: { + fontSize: '1.5rem', + fontWeight: 700 + }, + h5: { + fontSize: '1.2rem', + fontWeight: 700 + }, + h6: { + fontSize: '1rem', + fontWeight: 700 + }, + subtitle1: { + fontSize: '1rem', + fontWeight: 400 + }, + subtitle2: { + fontSize: '0.9rem', + fontWeight: 500 }, - MuiBottomNavigationAction: { - styleOverrides: { - root: { - '&.Mui-selected': { - color: '#ffffff' + body1: { + fontSize: '1rem', + fontWeight: 400 + }, + body2: { + fontSize: '0.9rem', + fontWeight: 400 + }, + button: { + fontSize: '1rem', + fontWeight: 700, + textTransform: 'uppercase' + }, + caption: { + fontSize: '0.8rem', + fontWeight: 400 + }, + overline: { + fontSize: '0.7rem', + fontWeight: 400, + textTransform: 'uppercase' + } + } +}); + +const theme = createTheme({ + ...{ + palette: { + mode: 'dark' + }, + components: { + MuiBottomNavigation: { + styleOverrides: { + root: { + backgroundColor: 'transparent', + borderTop: 'solid 1px #bcbcbc' + } + } + }, + MuiBottomNavigationAction: { + styleOverrides: { + root: { + '&.Mui-selected': { + color: '#ffffff' + } } } } } - } + }, + ...halloweenTheme }); Sentry.init({ From be9b167f6549b6913a820c5b3b2e6ead8b23519b Mon Sep 17 00:00:00 2001 From: Florian Bouron Date: Mon, 16 Oct 2023 20:56:33 +0200 Subject: [PATCH 2/6] Add hooks --- .../src/components/atoms/Logo/Logo.tsx | 38 +++++++++--- packages/extension/src/hooks/index.ts | 1 + .../src/hooks/useFeatureFlags/index.ts | 1 + .../hooks/useFeatureFlags/useFeatureFlags.ts | 30 ++++++++++ packages/extension/src/index.tsx | 60 ------------------- 5 files changed, 61 insertions(+), 69 deletions(-) create mode 100644 packages/extension/src/hooks/useFeatureFlags/index.ts create mode 100644 packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts diff --git a/packages/extension/src/components/atoms/Logo/Logo.tsx b/packages/extension/src/components/atoms/Logo/Logo.tsx index de388a110..8c2f7a18c 100644 --- a/packages/extension/src/components/atoms/Logo/Logo.tsx +++ b/packages/extension/src/components/atoms/Logo/Logo.tsx @@ -1,10 +1,30 @@ -import { FC, SVGProps } from 'react'; +import { FC, SVGProps, useMemo } from 'react'; + +import { useFeatureFlags } from '../../../hooks'; interface LogoProps extends SVGProps { isAnimated?: boolean; } export const Logo: FC = ({ isAnimated, ...rest }) => { + const { featureFlags } = useFeatureFlags(); + + const colors = useMemo(() => { + if ((featureFlags as any)['CITROUILLE_2K23']) { + return { + primary: '#FF7518', + secondary: '#FFA500', + tertiary: '#FFD700' + }; + } + //TODO: In another MR these colors will need to come from the template + return { + primary: '#00A8EA', + secondary: '#33D3F4', + tertiary: '#40EEFF' + }; + }, [featureFlags]); + if (isAnimated) { return ( = ({ isAnimated, ...rest }) => { } - - + + = ({ isAnimated, ...rest }) => { transform="translate(-.593 -.711)" /> = ({ isAnimated, ...rest }) => { return ( - - + + { + const [featureFlags, setFeatureFlags] = useState({}); + const [isLoading, setLoading] = useState(true); + + useEffect(() => { + const fetchFeatureFlags = async () => { + try { + const response = await fetch( + 'https://raw.githubusercontent.com/ThibautBremand/gw-feature-flags/main/featureFlags.json' + ); + if (!response.ok) { + throw new Error('Failed to fetch feature flags'); + } + + const flagsData = await response.json(); + setFeatureFlags(flagsData); + } catch (error) { + console.error(error); + } finally { + setLoading(false); + } + }; + + fetchFeatureFlags(); + }, []); + + return { featureFlags, isLoading }; +}; diff --git a/packages/extension/src/index.tsx b/packages/extension/src/index.tsx index 8b192a6ff..8eca772ec 100644 --- a/packages/extension/src/index.tsx +++ b/packages/extension/src/index.tsx @@ -46,66 +46,6 @@ const halloweenTheme = createTheme({ }, shape: { borderRadius: 8 // Adjust as needed - }, - typography: { - fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', - fontWeightRegular: 400, - fontWeightMedium: 500, - fontWeightBold: 700, - h1: { - fontSize: '2.5rem', - fontWeight: 700 - }, - h2: { - fontSize: '2rem', - fontWeight: 700 - }, - h3: { - fontSize: '1.8rem', - fontWeight: 700 - }, - h4: { - fontSize: '1.5rem', - fontWeight: 700 - }, - h5: { - fontSize: '1.2rem', - fontWeight: 700 - }, - h6: { - fontSize: '1rem', - fontWeight: 700 - }, - subtitle1: { - fontSize: '1rem', - fontWeight: 400 - }, - subtitle2: { - fontSize: '0.9rem', - fontWeight: 500 - }, - body1: { - fontSize: '1rem', - fontWeight: 400 - }, - body2: { - fontSize: '0.9rem', - fontWeight: 400 - }, - button: { - fontSize: '1rem', - fontWeight: 700, - textTransform: 'uppercase' - }, - caption: { - fontSize: '0.8rem', - fontWeight: 400 - }, - overline: { - fontSize: '0.7rem', - fontWeight: 400, - textTransform: 'uppercase' - } } }); From 46fe6d02e6190c99e58a8ea37c5305604780a8c1 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Mon, 16 Oct 2023 20:59:59 +0200 Subject: [PATCH 3/6] Add halloween theme & icons --- packages/extension/package.json | 1 + .../components/organisms/Header/Header.tsx | 38 +++++++++++++------ .../components/organisms/NavMenu/NavMenu.tsx | 7 +++- packages/extension/src/constants/colors.ts | 1 + .../extension/src/constants/navigation.tsx | 14 +++++-- packages/extension/src/index.tsx | 23 +++++------ yarn.lock | 5 +++ 7 files changed, 57 insertions(+), 32 deletions(-) diff --git a/packages/extension/package.json b/packages/extension/package.json index 8eff11006..fa8d4abc5 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -19,6 +19,7 @@ "react": "^17.0.2", "react-content-loader": "^6.2.0", "react-dom": "^17.0.2", + "react-icons": "^4.11.0", "react-infinite-scroll-component": "^6.1.0", "react-json-view": "^1.21.3", "react-lazy-load-image-component": "^1.6.0", diff --git a/packages/extension/src/components/organisms/Header/Header.tsx b/packages/extension/src/components/organisms/Header/Header.tsx index 01ab97e3e..c1ad003ef 100644 --- a/packages/extension/src/components/organisms/Header/Header.tsx +++ b/packages/extension/src/components/organisms/Header/Header.tsx @@ -6,6 +6,8 @@ import OutboundIcon from '@mui/icons-material/Outbound'; import { AppBar, Box, Button, IconButton, Toolbar, Tooltip, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import copyToClipboard from 'copy-to-clipboard'; +import { GiHangingSpider } from 'react-icons/gi'; +import { SiGhostery } from 'react-icons/si'; import { useNavigate } from 'react-router-dom'; import { @@ -43,6 +45,10 @@ export const Header: FC = ({ wallet: { name, publicAddress } }) => const truncatedAddress = useMemo(() => truncateAddress(publicAddress), [publicAddress]); + const isHalloween = useMemo(() => { + return process.env.REACT_APP_IS_HALLOWEEN === 'true'; + }, []); + const handleShare = useCallback(() => { copyToClipboard(publicAddress); setIsCopied(true); @@ -117,12 +123,16 @@ export const Header: FC = ({ wallet: { name, publicAddress } }) => alignItems: 'center' }} > - + {isHalloween ? ( + + ) : ( + + )} Send @@ -137,12 +147,16 @@ export const Header: FC = ({ wallet: { name, publicAddress } }) => }} onClick={handleReceive} > - + {isHalloween ? ( + // Halloween icon + ) : ( + + )} Receive diff --git a/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx b/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx index d9f1e40c6..bdb0d117e 100644 --- a/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx +++ b/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx @@ -4,9 +4,12 @@ import { BottomNavigation, BottomNavigationAction } from '@mui/material'; import { styled } from '@mui/system'; import { useNavigate } from 'react-router-dom'; -import { GEMWALLET_BLUE, navigation } from '../../../constants'; +import { GEMWALLET_BLUE, GEMWALLET_HALLOWEEN_ORANGE, navigation } from '../../../constants'; import { useNavBarPosition } from '../../../contexts'; +const isHalloween = process.env.REACT_APP_IS_HALLOWEEN === 'true'; +const backgroundColor = isHalloween ? GEMWALLET_HALLOWEEN_ORANGE : GEMWALLET_BLUE; + const defaultDecoration = { '--decoration-left': '50%', '--decoration-width': '0' @@ -25,7 +28,7 @@ const StyledBottomNavigation = styled(BottomNavigation)` left: var(--decoration-left); width: var(--decoration-width); height: 2px; - background: ${GEMWALLET_BLUE}; + background: ${backgroundColor}; transition: 300ms; border-radius: 2px; } diff --git a/packages/extension/src/constants/colors.ts b/packages/extension/src/constants/colors.ts index b8f2d886c..4d48c4592 100644 --- a/packages/extension/src/constants/colors.ts +++ b/packages/extension/src/constants/colors.ts @@ -1,3 +1,4 @@ export const SECONDARY_GRAY = '#797A7F'; export const ERROR_RED = '#F44336'; export const GEMWALLET_BLUE = '#87CEEB'; +export const GEMWALLET_HALLOWEEN_ORANGE = '#FFA500'; diff --git a/packages/extension/src/constants/navigation.tsx b/packages/extension/src/constants/navigation.tsx index 2aa9fc600..18b107bbc 100644 --- a/packages/extension/src/constants/navigation.tsx +++ b/packages/extension/src/constants/navigation.tsx @@ -1,29 +1,35 @@ +import React from 'react'; + import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import HistoryIcon from '@mui/icons-material/History'; import PhotoCameraBackIcon from '@mui/icons-material/PhotoCameraBack'; import SettingsIcon from '@mui/icons-material/Settings'; +import { FaGhost, FaHatWizard, FaSpider } from 'react-icons/fa'; +import { GiPumpkinLantern } from 'react-icons/gi'; import { HISTORY_PATH, HOME_PATH, NFT_VIEWER_PATH, SETTINGS_PATH } from './paths'; +const isHalloween = process.env.REACT_APP_IS_HALLOWEEN === 'true'; + export const navigation = [ { label: 'Tokens', pathname: HOME_PATH, - icon: + icon: isHalloween ? : }, { label: 'History', pathname: HISTORY_PATH, - icon: + icon: isHalloween ? : }, { label: 'NFTs', pathname: NFT_VIEWER_PATH, - icon: + icon: isHalloween ? : }, { label: 'Settings', pathname: SETTINGS_PATH, - icon: + icon: isHalloween ? : } ]; diff --git a/packages/extension/src/index.tsx b/packages/extension/src/index.tsx index 8eca772ec..19f5b661f 100644 --- a/packages/extension/src/index.tsx +++ b/packages/extension/src/index.tsx @@ -14,7 +14,7 @@ import { } from 'react-router-dom'; import App from './App'; -import { POPUP_HEIGHT, POPUP_WIDTH } from './constants'; +import { GEMWALLET_HALLOWEEN_ORANGE, POPUP_HEIGHT, POPUP_WIDTH } from './constants'; import { BrowserProvider, LedgerProvider, @@ -27,25 +27,20 @@ import { import reportWebVitals from './reportWebVitals'; import './index.css'; +const isHalloween = process.env.REACT_APP_IS_HALLOWEEN === 'true'; + const halloweenTheme = createTheme({ palette: { + mode: 'dark', primary: { - main: '#FF7518' // Vibrant Halloween orange + main: GEMWALLET_HALLOWEEN_ORANGE // Orange color for Halloween theme }, secondary: { - main: '#FFA500' // Standard orange - }, - text: { - primary: '#FFFFFF', // White text - secondary: '#FFD700' // Gold text + main: '#793D0D' // Darker brownish color for contrast with the orange }, - background: { - default: '#121212', // Dark background - paper: '#1E1E1E' // Slightly lighter background for paper elements + error: { + main: '#D32F2F' // A dark red color for errors, warnings, or important notifications } - }, - shape: { - borderRadius: 8 // Adjust as needed } }); @@ -74,7 +69,7 @@ const theme = createTheme({ } } }, - ...halloweenTheme + ...(isHalloween ? halloweenTheme : {}) }); Sentry.init({ diff --git a/yarn.lock b/yarn.lock index 8cf874779..29838710d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11142,6 +11142,11 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6" integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA== +react-icons@^4.11.0: + version "4.11.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.11.0.tgz#4b0e31c9bfc919608095cc429c4f1846f4d66c65" + integrity sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA== + react-infinite-scroll-component@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz#7e511e7aa0f728ac3e51f64a38a6079ac522407f" From ad264850e2652f33e4b52e935f3f49f5243a5dc3 Mon Sep 17 00:00:00 2001 From: Florian Bouron Date: Mon, 16 Oct 2023 21:39:47 +0200 Subject: [PATCH 4/6] Make use of feature flag --- .../components/organisms/Header/Header.tsx | 7 +- .../components/organisms/NavMenu/NavMenu.tsx | 78 +++++++---- .../extension/src/constants/navigation.tsx | 12 +- .../contexts/NavBarContext/NavBarContext.tsx | 16 ++- packages/extension/src/index.tsx | 121 +++++++++--------- 5 files changed, 138 insertions(+), 96 deletions(-) diff --git a/packages/extension/src/components/organisms/Header/Header.tsx b/packages/extension/src/components/organisms/Header/Header.tsx index c1ad003ef..84356de3a 100644 --- a/packages/extension/src/components/organisms/Header/Header.tsx +++ b/packages/extension/src/components/organisms/Header/Header.tsx @@ -17,7 +17,7 @@ import { SEND_PATH, RECEIVE_PATH } from '../../../constants'; -import { useTimeout } from '../../../hooks'; +import { useFeatureFlags, useTimeout } from '../../../hooks'; import { WalletLedger } from '../../../types'; import { truncateAddress, truncateWalletName } from '../../../utils'; import { WalletIcon } from '../../atoms'; @@ -40,14 +40,15 @@ export interface HeaderProps { export const Header: FC = ({ wallet: { name, publicAddress } }) => { const navigate = useNavigate(); const setTimeout = useTimeout(2000); + const { featureFlags } = useFeatureFlags(); const [isCopied, setIsCopied] = useState(false); const truncatedAddress = useMemo(() => truncateAddress(publicAddress), [publicAddress]); const isHalloween = useMemo(() => { - return process.env.REACT_APP_IS_HALLOWEEN === 'true'; - }, []); + return (featureFlags as any)['CITROUILLE_2K23']; + }, [featureFlags]); const handleShare = useCallback(() => { copyToClipboard(publicAddress); diff --git a/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx b/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx index bdb0d117e..c222d5afa 100644 --- a/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx +++ b/packages/extension/src/components/organisms/NavMenu/NavMenu.tsx @@ -1,39 +1,23 @@ -import { CSSProperties, FC, MouseEvent, useEffect } from 'react'; +import { CSSProperties, FC, MouseEvent, useEffect, useMemo } from 'react'; import { BottomNavigation, BottomNavigationAction } from '@mui/material'; import { styled } from '@mui/system'; +import { FaGhost, FaHatWizard, FaSpider } from 'react-icons/fa'; +import { GiPumpkinLantern } from 'react-icons/gi'; import { useNavigate } from 'react-router-dom'; -import { GEMWALLET_BLUE, GEMWALLET_HALLOWEEN_ORANGE, navigation } from '../../../constants'; +import { + GEMWALLET_BLUE, + GEMWALLET_HALLOWEEN_ORANGE, + navigation as navigationConstant +} from '../../../constants'; import { useNavBarPosition } from '../../../contexts'; -const isHalloween = process.env.REACT_APP_IS_HALLOWEEN === 'true'; -const backgroundColor = isHalloween ? GEMWALLET_HALLOWEEN_ORANGE : GEMWALLET_BLUE; - const defaultDecoration = { '--decoration-left': '50%', '--decoration-width': '0' }; -const StyledBottomNavigation = styled(BottomNavigation)` - ${defaultDecoration} - position: relative; - border-top: none !important; - box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.35); - - &::after { - content: ''; - position: absolute; - top: 0; - left: var(--decoration-left); - width: var(--decoration-width); - height: 2px; - background: ${backgroundColor}; - transition: 300ms; - border-radius: 2px; - } -`; - const StyledBottomNavigationAction = styled(BottomNavigationAction)` border-top: none !important; box-shadow: none !important; @@ -45,7 +29,51 @@ export interface NavMenuProps { export const NavMenu: FC = ({ indexDefaultNav }) => { const navigate = useNavigate(); - const { navBarPosition, setNavBarPosition } = useNavBarPosition(); + const { navBarPosition, setNavBarPosition, isHalloween } = useNavBarPosition(); + + const StyledBottomNavigation = useMemo(() => { + const backgroundColor = isHalloween ? GEMWALLET_HALLOWEEN_ORANGE : GEMWALLET_BLUE; + + return styled(BottomNavigation)` + ${defaultDecoration} + position: relative; + border-top: none !important; + box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.35); + + &::after { + content: ''; + position: absolute; + top: 0; + left: var(--decoration-left); + width: var(--decoration-width); + height: 2px; + background: ${backgroundColor}; + transition: 300ms; + border-radius: 2px; + } + `; + }, [isHalloween]); + + const navigation = useMemo(() => { + if (!isHalloween) { + return navigationConstant; + } + const navigationHalloween = [ + , + , + , + + ]; + + if (navigationConstant.length !== navigationHalloween.length) { + throw new Error('navigation constant and navigation Halloween must have the same length'); + } + + return navigationConstant.map((navItem, index) => ({ + ...navItem, + icon: navigationHalloween[index] + })); + }, [isHalloween]); useEffect(() => { if (indexDefaultNav !== -1) { diff --git a/packages/extension/src/constants/navigation.tsx b/packages/extension/src/constants/navigation.tsx index 18b107bbc..32716deb3 100644 --- a/packages/extension/src/constants/navigation.tsx +++ b/packages/extension/src/constants/navigation.tsx @@ -4,32 +4,28 @@ import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import HistoryIcon from '@mui/icons-material/History'; import PhotoCameraBackIcon from '@mui/icons-material/PhotoCameraBack'; import SettingsIcon from '@mui/icons-material/Settings'; -import { FaGhost, FaHatWizard, FaSpider } from 'react-icons/fa'; -import { GiPumpkinLantern } from 'react-icons/gi'; import { HISTORY_PATH, HOME_PATH, NFT_VIEWER_PATH, SETTINGS_PATH } from './paths'; -const isHalloween = process.env.REACT_APP_IS_HALLOWEEN === 'true'; - export const navigation = [ { label: 'Tokens', pathname: HOME_PATH, - icon: isHalloween ? : + icon: }, { label: 'History', pathname: HISTORY_PATH, - icon: isHalloween ? : + icon: }, { label: 'NFTs', pathname: NFT_VIEWER_PATH, - icon: isHalloween ? : + icon: }, { label: 'Settings', pathname: SETTINGS_PATH, - icon: isHalloween ? : + icon: } ]; diff --git a/packages/extension/src/contexts/NavBarContext/NavBarContext.tsx b/packages/extension/src/contexts/NavBarContext/NavBarContext.tsx index ea6caa2b7..049643ecb 100644 --- a/packages/extension/src/contexts/NavBarContext/NavBarContext.tsx +++ b/packages/extension/src/contexts/NavBarContext/NavBarContext.tsx @@ -1,7 +1,9 @@ -import { createContext, FC, useContext, useState } from 'react'; +import { createContext, FC, useContext, useMemo, useState } from 'react'; import * as Sentry from '@sentry/react'; +import { useFeatureFlags } from '../../hooks'; + interface NavBarPosition { left: string; width: string; @@ -10,6 +12,7 @@ interface NavBarPosition { interface NavBarPositionContextType { setNavBarPosition: (position: NavBarPosition) => void; navBarPosition: NavBarPosition; + isHalloween: boolean; } const defaultPosition = { @@ -19,15 +22,22 @@ const defaultPosition = { const NavBarPositionContext = createContext({ setNavBarPosition: () => {}, - navBarPosition: defaultPosition + navBarPosition: defaultPosition, + isHalloween: false }); const NavBarPositionProvider: FC = ({ children }) => { const [navBarPosition, setNavBarPosition] = useState(defaultPosition); + const { featureFlags } = useFeatureFlags(); + + const isHalloween = useMemo(() => { + return (featureFlags as any)['CITROUILLE_2K23']; + }, [featureFlags]); const contextValue: NavBarPositionContextType = { navBarPosition, - setNavBarPosition + setNavBarPosition, + isHalloween }; return ( diff --git a/packages/extension/src/index.tsx b/packages/extension/src/index.tsx index 19f5b661f..3ebf368cb 100644 --- a/packages/extension/src/index.tsx +++ b/packages/extension/src/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import Paper from '@mui/material/Paper'; import { ThemeProvider, createTheme } from '@mui/material/styles'; @@ -24,11 +24,10 @@ import { TransactionProgressProvider, WalletProvider } from './contexts'; +import { useFeatureFlags } from './hooks'; import reportWebVitals from './reportWebVitals'; import './index.css'; -const isHalloween = process.env.REACT_APP_IS_HALLOWEEN === 'true'; - const halloweenTheme = createTheme({ palette: { mode: 'dark', @@ -44,34 +43,6 @@ const halloweenTheme = createTheme({ } }); -const theme = createTheme({ - ...{ - palette: { - mode: 'dark' - }, - components: { - MuiBottomNavigation: { - styleOverrides: { - root: { - backgroundColor: 'transparent', - borderTop: 'solid 1px #bcbcbc' - } - } - }, - MuiBottomNavigationAction: { - styleOverrides: { - root: { - '&.Mui-selected': { - color: '#ffffff' - } - } - } - } - } - }, - ...(isHalloween ? halloweenTheme : {}) -}); - Sentry.init({ dsn: process.env.REACT_APP_SENTRY_DSN, release: 'gemwallet-extension@' + process.env.REACT_APP_VERSION, @@ -96,32 +67,68 @@ Sentry.init({ replaysOnErrorSampleRate: process.env.NODE_ENV === 'development' ? 0.0 : 1.0 }); -ReactDOM.render( - - - - - - - - - - - - - - - - - - - - - - - , - document.getElementById('root') -); +const GemWallet = () => { + const { featureFlags } = useFeatureFlags(); + const theme = useMemo( + () => + createTheme({ + ...{ + palette: { + mode: 'dark' + }, + components: { + MuiBottomNavigation: { + styleOverrides: { + root: { + backgroundColor: 'transparent', + borderTop: 'solid 1px #bcbcbc' + } + } + }, + MuiBottomNavigationAction: { + styleOverrides: { + root: { + '&.Mui-selected': { + color: '#ffffff' + } + } + } + } + } + }, + ...((featureFlags as any)['CITROUILLE_2K23'] ? halloweenTheme : {}) + }), + [featureFlags] + ); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +ReactDOM.render(, document.getElementById('root')); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) From 207766e8db283752513457f5c1382ea70d787407 Mon Sep 17 00:00:00 2001 From: Florian Bouron Date: Wed, 18 Oct 2023 20:46:26 +0200 Subject: [PATCH 5/6] Add caching to useFeatureFlags --- .../components/organisms/Header/Header.tsx | 2 +- .../src/components/pages/Welcome/Welcome.tsx | 2 +- .../extension/src/constants/navigation.tsx | 2 - packages/extension/src/constants/storage.ts | 4 ++ .../hooks/useFeatureFlags/useFeatureFlags.ts | 55 ++++++++++++++++--- packages/extension/src/index.tsx | 6 +- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/packages/extension/src/components/organisms/Header/Header.tsx b/packages/extension/src/components/organisms/Header/Header.tsx index 84356de3a..6f754d351 100644 --- a/packages/extension/src/components/organisms/Header/Header.tsx +++ b/packages/extension/src/components/organisms/Header/Header.tsx @@ -149,7 +149,7 @@ export const Header: FC = ({ wallet: { name, publicAddress } }) => onClick={handleReceive} > {isHalloween ? ( - // Halloween icon + ) : ( { }} > - + GemWallet diff --git a/packages/extension/src/constants/navigation.tsx b/packages/extension/src/constants/navigation.tsx index 32716deb3..2aa9fc600 100644 --- a/packages/extension/src/constants/navigation.tsx +++ b/packages/extension/src/constants/navigation.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import HistoryIcon from '@mui/icons-material/History'; import PhotoCameraBackIcon from '@mui/icons-material/PhotoCameraBack'; diff --git a/packages/extension/src/constants/storage.ts b/packages/extension/src/constants/storage.ts index 2bf668c9a..62dcaf1fa 100644 --- a/packages/extension/src/constants/storage.ts +++ b/packages/extension/src/constants/storage.ts @@ -1,6 +1,7 @@ // Keys export const STORAGE_CURRENT_WINDOW_ID = 'currentWindowId'; export const STORAGE_CUSTOM_NETWORKS = 'customNetworks'; +export const STORAGE_FEATURE_FLAGS = 'featureFlags'; export const STORAGE_NETWORK = 'network'; export const STORAGE_PERMISSION_SUBMIT_BULK = 'permissionSubmitBulkTransactions'; export const STORAGE_REMEMBER_SESSION = 'rememberSession'; @@ -8,3 +9,6 @@ export const STORAGE_SELECTED_WALLET = 'selectedWallet'; export const STORAGE_STATE_TRANSACTION = 'hasTxInProgress'; export const STORAGE_TRUSTED_APPS = 'trustedApps'; export const STORAGE_WALLETS = 'wallets'; + +// TTL +export const TTL_FEATURE_FLAGS = 60 * 60 * 1000; // 1 hour diff --git a/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts b/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts index 26cc8be73..675fe7b16 100644 --- a/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts +++ b/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts @@ -1,23 +1,60 @@ import { useState, useEffect } from 'react'; +import { STORAGE_FEATURE_FLAGS, TTL_FEATURE_FLAGS } from '../../constants'; + +interface FeatureFlagsData { + data: Record; + expiration: number; +} + +const isDataWithinTTL = (data: FeatureFlagsData) => { + return new Date().getTime() - data.expiration < TTL_FEATURE_FLAGS; +}; + +const fetchAndSaveFeatureFlags = async (): Promise> => { + if (!process.env.REACT_APP_FEATURE_FLAGS_URL) { + return {}; + } + + const response = await fetch(process.env.REACT_APP_FEATURE_FLAGS_URL); + + if (!response.ok) { + return {}; + } + + const data: Record = await response.json(); + const featureFlagsData: FeatureFlagsData = { + data, + expiration: new Date().getTime() + TTL_FEATURE_FLAGS + }; + + localStorage.setItem(STORAGE_FEATURE_FLAGS, JSON.stringify(featureFlagsData)); + return data; +}; + export const useFeatureFlags = () => { - const [featureFlags, setFeatureFlags] = useState({}); + const [featureFlags, setFeatureFlags] = useState>({}); const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchFeatureFlags = async () => { try { - const response = await fetch( - 'https://raw.githubusercontent.com/ThibautBremand/gw-feature-flags/main/featureFlags.json' - ); - if (!response.ok) { - throw new Error('Failed to fetch feature flags'); + const cachedData = localStorage.getItem(STORAGE_FEATURE_FLAGS); + + if (cachedData) { + const parsedData: FeatureFlagsData = JSON.parse(cachedData); + + if (isDataWithinTTL(parsedData)) { + setFeatureFlags(parsedData.data); + setLoading(false); + return; + } } - const flagsData = await response.json(); - setFeatureFlags(flagsData); + const data = await fetchAndSaveFeatureFlags(); + setFeatureFlags(data); } catch (error) { - console.error(error); + console.error('Error fetching feature flags:', error); } finally { setLoading(false); } diff --git a/packages/extension/src/index.tsx b/packages/extension/src/index.tsx index 3ebf368cb..9937f8479 100644 --- a/packages/extension/src/index.tsx +++ b/packages/extension/src/index.tsx @@ -32,13 +32,13 @@ const halloweenTheme = createTheme({ palette: { mode: 'dark', primary: { - main: GEMWALLET_HALLOWEEN_ORANGE // Orange color for Halloween theme + main: GEMWALLET_HALLOWEEN_ORANGE }, secondary: { - main: '#793D0D' // Darker brownish color for contrast with the orange + main: '#793D0D' }, error: { - main: '#D32F2F' // A dark red color for errors, warnings, or important notifications + main: '#D32F2F' } } }); From 12fa7ed417a9f27028630225fc281b8a4627ed35 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Thu, 19 Oct 2023 10:22:05 +0200 Subject: [PATCH 6/6] Use session storage for flags --- .../extension/src/hooks/useFeatureFlags/useFeatureFlags.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts b/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts index 675fe7b16..a9c4449f2 100644 --- a/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts +++ b/packages/extension/src/hooks/useFeatureFlags/useFeatureFlags.ts @@ -1,6 +1,7 @@ import { useState, useEffect } from 'react'; import { STORAGE_FEATURE_FLAGS, TTL_FEATURE_FLAGS } from '../../constants'; +import { loadFromChromeSessionStorage, saveInChromeSessionStorage } from '../../utils'; interface FeatureFlagsData { data: Record; @@ -28,7 +29,7 @@ const fetchAndSaveFeatureFlags = async (): Promise> => { expiration: new Date().getTime() + TTL_FEATURE_FLAGS }; - localStorage.setItem(STORAGE_FEATURE_FLAGS, JSON.stringify(featureFlagsData)); + saveInChromeSessionStorage(STORAGE_FEATURE_FLAGS, JSON.stringify(featureFlagsData)); return data; }; @@ -39,7 +40,7 @@ export const useFeatureFlags = () => { useEffect(() => { const fetchFeatureFlags = async () => { try { - const cachedData = localStorage.getItem(STORAGE_FEATURE_FLAGS); + const cachedData = await loadFromChromeSessionStorage(STORAGE_FEATURE_FLAGS); if (cachedData) { const parsedData: FeatureFlagsData = JSON.parse(cachedData);