Commit 516c8b05 authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

feat: add group flag toggles (#4502)

parent 8502f9e3
import { FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags' import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar' import { NavBarVariant, useNavBarFlag } from 'featureFlags/flags/navBar'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft' import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign' import { RedesignVariant, useRedesignFlag } from 'featureFlags/flags/redesign'
import { TokensVariant, useTokensFlag } from 'featureFlags/flags/tokens' import { TokensVariant, useTokensFlag } from 'featureFlags/flags/tokens'
import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety' import { TokenSafetyVariant, useTokenSafetyFlag } from 'featureFlags/flags/tokenSafety'
import { TokensNetworkFilterVariant, useTokensNetworkFilterFlag } from 'featureFlags/flags/tokensNetworkFilter' import { TokensNetworkFilterVariant, useTokensNetworkFilterFlag } from 'featureFlags/flags/tokensNetworkFilter'
import { useAtomValue } from 'jotai/utils' import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { ReactNode, useState } from 'react' import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
import { X } from 'react-feather' import { X } from 'react-feather'
import { useModalIsOpen, useToggleFeatureFlags } from 'state/application/hooks' import { useModalIsOpen, useToggleFeatureFlags } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
...@@ -45,7 +45,14 @@ const Row = styled.div` ...@@ -45,7 +45,14 @@ const Row = styled.div`
const CloseButton = styled.button` const CloseButton = styled.button`
cursor: pointer; cursor: pointer;
background: 'transparent'; background: transparent;
border: none;
color: ${({ theme }) => theme.textPrimary};
`
const ToggleButton = styled.button`
cursor: pointer;
background: transparent;
border: none; border: none;
color: ${({ theme }) => theme.textPrimary}; color: ${({ theme }) => theme.textPrimary};
` `
...@@ -59,7 +66,6 @@ const Header = styled(Row)` ...@@ -59,7 +66,6 @@ const Header = styled(Row)`
const FlagName = styled.span` const FlagName = styled.span`
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
padding-left: 8px;
color: ${({ theme }) => theme.textPrimary}; color: ${({ theme }) => theme.textPrimary};
` `
const FlagGroupName = styled.span` const FlagGroupName = styled.span`
...@@ -115,17 +121,53 @@ function Variant({ option }: { option: string }) { ...@@ -115,17 +121,53 @@ function Variant({ option }: { option: string }) {
return <option value={option}>{option}</option> return <option value={option}>{option}</option>
} }
function FeatureFlagOption({ interface FeatureFlagProps {
variants, variant: Record<string, string>
featureFlag,
value,
label,
}: {
variants: string[]
featureFlag: FeatureFlag featureFlag: FeatureFlag
value: string value: string
label: string label: string
}) { }
function FeatureFlagGroup({ name, children }: PropsWithChildren<{ name: string }>) {
// type FeatureFlagOption = { props: FeatureFlagProps }
const togglableOptions = Children.toArray(children)
.filter<ReactElement<FeatureFlagProps>>(
(child): child is ReactElement<FeatureFlagProps> =>
child instanceof Object && 'type' in child && child.type === FeatureFlagOption
)
.map(({ props }) => props)
.filter(({ variant }) => {
const values = Object.values(variant)
return values.includes(BaseVariant.Control) && values.includes(BaseVariant.Enabled)
})
const setFeatureFlags = useUpdateAtom(featureFlagSettings)
const allEnabled = togglableOptions.every(({ value }) => value === BaseVariant.Enabled)
const onToggle = useCallback(() => {
setFeatureFlags((flags) => ({
...flags,
...togglableOptions.reduce(
(flags, { featureFlag }) => ({
...flags,
[featureFlag]: allEnabled ? BaseVariant.Control : BaseVariant.Enabled,
}),
{}
),
}))
}, [allEnabled, setFeatureFlags, togglableOptions])
return (
<>
<Row key={name}>
<FlagGroupName>{name}</FlagGroupName>
<ToggleButton onClick={onToggle}>{allEnabled ? 'Disable' : 'Enable'} group</ToggleButton>
</Row>
{children}
</>
)
}
function FeatureFlagOption({ variant, featureFlag, value, label }: FeatureFlagProps) {
const updateFlag = useUpdateFlag() const updateFlag = useUpdateFlag()
const [count, setCount] = useState(0) const [count, setCount] = useState(0)
const featureFlags = useAtomValue(featureFlagSettings) const featureFlags = useAtomValue(featureFlagSettings)
...@@ -144,7 +186,7 @@ function FeatureFlagOption({ ...@@ -144,7 +186,7 @@ function FeatureFlagOption({
}} }}
value={featureFlags[featureFlag]} value={featureFlags[featureFlag]}
> >
{variants.map((variant) => ( {Object.values(variant).map((variant) => (
<Variant key={variant} option={variant} /> <Variant key={variant} option={variant} />
))} ))}
</FlagVariantSelection> </FlagVariantSelection>
...@@ -164,45 +206,42 @@ export default function FeatureFlagModal() { ...@@ -164,45 +206,42 @@ export default function FeatureFlagModal() {
<X size={24} /> <X size={24} />
</CloseButton> </CloseButton>
</Header> </Header>
<FlagGroupName>Phase 1</FlagGroupName> <FeatureFlagGroup name="Phase 0">
<FeatureFlagOption <FeatureFlagOption
variants={Object.values(NftVariant)} variant={RedesignVariant}
value={useNftFlag()} value={useRedesignFlag()}
featureFlag={FeatureFlag.nft} featureFlag={FeatureFlag.redesign}
label="NFTs" label="Redesign"
/> />
<FlagGroupName>Phase 0</FlagGroupName> <FeatureFlagOption
<FeatureFlagOption variant={NavBarVariant}
variants={Object.values(RedesignVariant)} value={useNavBarFlag()}
value={useRedesignFlag()} featureFlag={FeatureFlag.navBar}
featureFlag={FeatureFlag.redesign} label="NavBar"
label="Redesign" />
/> <FeatureFlagOption
<FeatureFlagOption variant={TokensVariant}
variants={Object.values(NavBarVariant)} value={useTokensFlag()}
value={useNavBarFlag()} featureFlag={FeatureFlag.tokens}
featureFlag={FeatureFlag.navBar} label="Tokens"
label="NavBar" />
/> <FeatureFlagOption
<FeatureFlagOption variant={TokensNetworkFilterVariant}
variants={Object.values(TokensVariant)} value={useTokensNetworkFilterFlag()}
value={useTokensFlag()} featureFlag={FeatureFlag.tokensNetworkFilter}
featureFlag={FeatureFlag.tokens} label="Tokens Network Filter"
label="Tokens" />
/> <FeatureFlagOption
<FeatureFlagOption variant={TokenSafetyVariant}
variants={Object.values(TokensNetworkFilterVariant)} value={useTokenSafetyFlag()}
value={useTokensNetworkFilterFlag()} featureFlag={FeatureFlag.tokenSafety}
featureFlag={FeatureFlag.tokensNetworkFilter} label="Token Safety"
label="Tokens Network Filter" />
/> </FeatureFlagGroup>
<FeatureFlagOption <FeatureFlagGroup name="Phase 1">
variants={Object.values(TokenSafetyVariant)} <FeatureFlagOption variant={NftVariant} value={useNftFlag()} featureFlag={FeatureFlag.nft} label="NFTs" />
value={useTokenSafetyFlag()} </FeatureFlagGroup>
featureFlag={FeatureFlag.tokenSafety} <SaveButton onClick={() => window.location.reload()}>Reload</SaveButton>
label="Token Safety"
/>
<SaveButton onClick={() => window.location.reload()}>Save Settings</SaveButton>
</Modal> </Modal>
) )
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment