Skip to content

Commit

Permalink
feat: initial FeatureFlagProvider (Uniswap#4248)
Browse files Browse the repository at this point in the history
* initial

* add to index

* show more logic

* split up

* nvm combine

* combine more

* loading state for the app

* no conditional

* rm var

* comment

* move comment

* add control specifically
  • Loading branch information
vm authored Aug 2, 2022
1 parent 1f2ffa1 commit 134879e
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 59 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
},
"dependencies": {
"@amplitude/analytics-browser": "^0.5.1",
"@amplitude/experiment-js-client": "^1.5.4",
"@coinbase/wallet-sdk": "^3.3.0",
"@fontsource/ibm-plex-mono": "^4.5.1",
"@fontsource/inter": "^4.5.1",
Expand Down
66 changes: 66 additions & 0 deletions src/featureFlag/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { createContext, ReactNode, useContext } from 'react'

interface FeatureFlagsContextType {
isLoaded: boolean
flags: Record<string, string>
}

const FeatureFlagContext = createContext<FeatureFlagsContextType>({ isLoaded: false, flags: {} })

export function useFeatureFlagsContext(): FeatureFlagsContextType {
const context = useContext(FeatureFlagContext)
if (!context) {
throw Error('Feature flag hooks can only be used by children of FeatureFlagProvider.')
} else {
return context
}
}

export function FeatureFlagsProvider({ children }: { children: ReactNode }) {
// TODO(vm): `isLoaded` to `true` so `App.tsx` will render. Later, this will be dependent on
// flags loading from Amplitude, with a timeout.
const value = {
isLoaded: true,
flags: {
phase0: 'control',
phase1: 'control',
},
}
return <FeatureFlagContext.Provider value={value}>{children}</FeatureFlagContext.Provider>
}

export function useFeatureFlagsIsLoaded(): boolean {
return useFeatureFlagsContext().isLoaded
}

// feature flag hooks

enum Phase0Variant {
Control = 'Control',
Enabled = 'Enabled',
}

export function usePhase0Flag(): Phase0Variant {
switch (useFeatureFlagsContext().flags['phase0']) {
case 'enabled':
return Phase0Variant.Enabled
case 'control':
default:
return Phase0Variant.Control
}
}

enum Phase1Variant {
Control = 'Control',
Enabled = 'Enabled',
}

export function usePhase1Flag(): Phase1Variant {
switch (useFeatureFlagsContext().flags['phase1']) {
case 'enabled':
return Phase1Variant.Enabled
case 'control':
default:
return Phase1Variant.Control
}
}
33 changes: 18 additions & 15 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'inter-ui'
import 'polyfills'
import 'components/analytics'

import { FeatureFlagsProvider } from 'featureFlag'
import { BlockNumberProvider } from 'lib/hooks/useBlockNumber'
import { MulticallUpdater } from 'lib/state/multicall'
import { StrictMode } from 'react'
Expand Down Expand Up @@ -47,21 +48,23 @@ const container = document.getElementById('root') as HTMLElement
createRoot(container).render(
<StrictMode>
<Provider store={store}>
<HashRouter>
<LanguageProvider>
<Web3Provider>
<Blocklist>
<BlockNumberProvider>
<Updaters />
<ThemeProvider>
<ThemedGlobalStyle />
<App />
</ThemeProvider>
</BlockNumberProvider>
</Blocklist>
</Web3Provider>
</LanguageProvider>
</HashRouter>
<FeatureFlagsProvider>
<HashRouter>
<LanguageProvider>
<Web3Provider>
<Blocklist>
<BlockNumberProvider>
<Updaters />
<ThemeProvider>
<ThemedGlobalStyle />
<App />
</ThemeProvider>
</BlockNumberProvider>
</Blocklist>
</Web3Provider>
</LanguageProvider>
</HashRouter>
</FeatureFlagsProvider>
</Provider>
</StrictMode>
)
Expand Down
92 changes: 50 additions & 42 deletions src/pages/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PageName } from 'components/AmplitudeAnalytics/constants'
import { Trace } from 'components/AmplitudeAnalytics/Trace'
import Loader from 'components/Loader'
import TopLevelModals from 'components/TopLevelModals'
import { useFeatureFlagsIsLoaded } from 'featureFlag'
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
import { lazy, Suspense } from 'react'
import { useEffect } from 'react'
Expand Down Expand Up @@ -81,8 +82,11 @@ function getCurrentPageFromLocation(locationPathname: string): PageName | undefi
}

export default function App() {
const isLoaded = useFeatureFlagsIsLoaded()

const { pathname } = useLocation()
const currentPage = getCurrentPageFromLocation(pathname)

useAnalyticsReporter()
initializeAnalytics()

Expand All @@ -104,48 +108,52 @@ export default function App() {
<Polling />
<TopLevelModals />
<Suspense fallback={<Loader />}>
<Routes>
<Route path="vote/*" element={<Vote />} />
<Route path="create-proposal" element={<Navigate to="/vote/create-proposal" replace />} />
<Route path="claim" element={<OpenClaimAddressModalAndRedirectToSwap />} />
<Route path="uni" element={<Earn />} />
<Route path="uni/:currencyIdA/:currencyIdB" element={<Manage />} />

<Route path="send" element={<RedirectPathToSwapOnly />} />
<Route path="swap/:outputCurrency" element={<RedirectToSwap />} />
<Route path="swap" element={<Swap />} />

<Route path="pool/v2/find" element={<PoolFinder />} />
<Route path="pool/v2" element={<PoolV2 />} />
<Route path="pool" element={<Pool />} />
<Route path="pool/:tokenId" element={<PositionPage />} />

<Route path="add/v2" element={<RedirectDuplicateTokenIdsV2 />}>
<Route path=":currencyIdA" />
<Route path=":currencyIdA/:currencyIdB" />
</Route>
<Route path="add" element={<RedirectDuplicateTokenIds />}>
{/* this is workaround since react-router-dom v6 doesn't support optional parameters any more */}
<Route path=":currencyIdA" />
<Route path=":currencyIdA/:currencyIdB" />
<Route path=":currencyIdA/:currencyIdB/:feeAmount" />
</Route>

<Route path="increase" element={<AddLiquidity />}>
<Route path=":currencyIdA" />
<Route path=":currencyIdA/:currencyIdB" />
<Route path=":currencyIdA/:currencyIdB/:feeAmount" />
<Route path=":currencyIdA/:currencyIdB/:feeAmount/:tokenId" />
</Route>

<Route path="remove/v2/:currencyIdA/:currencyIdB" element={<RemoveLiquidity />} />
<Route path="remove/:tokenId" element={<RemoveLiquidityV3 />} />

<Route path="migrate/v2" element={<MigrateV2 />} />
<Route path="migrate/v2/:address" element={<MigrateV2Pair />} />

<Route path="*" element={<RedirectPathToSwapOnly />} />
</Routes>
{isLoaded ? (
<Routes>
<Route path="vote/*" element={<Vote />} />
<Route path="create-proposal" element={<Navigate to="/vote/create-proposal" replace />} />
<Route path="claim" element={<OpenClaimAddressModalAndRedirectToSwap />} />
<Route path="uni" element={<Earn />} />
<Route path="uni/:currencyIdA/:currencyIdB" element={<Manage />} />

<Route path="send" element={<RedirectPathToSwapOnly />} />
<Route path="swap/:outputCurrency" element={<RedirectToSwap />} />
<Route path="swap" element={<Swap />} />

<Route path="pool/v2/find" element={<PoolFinder />} />
<Route path="pool/v2" element={<PoolV2 />} />
<Route path="pool" element={<Pool />} />
<Route path="pool/:tokenId" element={<PositionPage />} />

<Route path="add/v2" element={<RedirectDuplicateTokenIdsV2 />}>
<Route path=":currencyIdA" />
<Route path=":currencyIdA/:currencyIdB" />
</Route>
<Route path="add" element={<RedirectDuplicateTokenIds />}>
{/* this is workaround since react-router-dom v6 doesn't support optional parameters any more */}
<Route path=":currencyIdA" />
<Route path=":currencyIdA/:currencyIdB" />
<Route path=":currencyIdA/:currencyIdB/:feeAmount" />
</Route>

<Route path="increase" element={<AddLiquidity />}>
<Route path=":currencyIdA" />
<Route path=":currencyIdA/:currencyIdB" />
<Route path=":currencyIdA/:currencyIdB/:feeAmount" />
<Route path=":currencyIdA/:currencyIdB/:feeAmount/:tokenId" />
</Route>

<Route path="remove/v2/:currencyIdA/:currencyIdB" element={<RemoveLiquidity />} />
<Route path="remove/:tokenId" element={<RemoveLiquidityV3 />} />

<Route path="migrate/v2" element={<MigrateV2 />} />
<Route path="migrate/v2/:address" element={<MigrateV2Pair />} />

<Route path="*" element={<RedirectPathToSwapOnly />} />
</Routes>
) : (
<Loader />
)}
</Suspense>
<Marginer />
</BodyWrapper>
Expand Down
25 changes: 23 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
"@amplitude/ua-parser-js" "^0.7.31"
tslib "^2.3.1"

"@amplitude/[email protected]":
version "1.4.5"
resolved "https://registry.yarnpkg.com/@amplitude/analytics-connector/-/analytics-connector-1.4.5.tgz#07e9375101332bd8b6f15e39e70f31f287e87ad0"
integrity sha512-ELAP6ivg+13uSk+TOirGZE/92M+tTbeiQ/i7eXgDO4Hiy00Abf/UxO/rp9WovtxCyeFYTILrujEYxPv5cRQmFw==
dependencies:
"@amplitude/ua-parser-js" "0.7.31"

"@amplitude/analytics-core@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@amplitude/analytics-core/-/analytics-core-0.4.1.tgz#0e1a7ae6470bcec277a0389e16dd110519f6750e"
Expand All @@ -25,7 +32,16 @@
resolved "https://registry.yarnpkg.com/@amplitude/analytics-types/-/analytics-types-0.3.0.tgz#3626f2907e4dd353f0ca3a21b847d7560a1c4da9"
integrity sha512-Hu/emt6mEpSGmOnLyqZGe75a5BNH3j9WJWBDFMBOZ1gLsbrxKtrb6w3EQy6yRROZ8Uyn04wC0veVUYyETwH3vg==

"@amplitude/ua-parser-js@^0.7.31":
"@amplitude/experiment-js-client@^1.5.4":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@amplitude/experiment-js-client/-/experiment-js-client-1.5.4.tgz#a1cb12282d368b0373db66954c5263a510c97e87"
integrity sha512-ABFR46yibEByKIr2rFXPQwYLnvtVRjFgCSGZuh4K/MwBflGwHu+KAu6beJl+Pt/vZXo2Ak7kYip5LikstTqsgA==
dependencies:
"@amplitude/analytics-connector" "1.4.5"
base64-js "1.5.1"
unfetch "4.1.0"

"@amplitude/[email protected]", "@amplitude/ua-parser-js@^0.7.31":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@amplitude/ua-parser-js/-/ua-parser-js-0.7.31.tgz#749bf7cb633cfcc7ff3c10805bad7c5f6fbdbc61"
integrity sha512-+z8UGRaj13Pt5NDzOnkTBy49HE2CX64jeL0ArB86HAtilpnfkPB7oqkigN7Lf2LxscMg4QhFD7mmCfedh3rqTg==
Expand Down Expand Up @@ -5810,7 +5826,7 @@ base-x@^3.0.2:
dependencies:
safe-buffer "^5.0.1"

base64-js@^1.0.2, base64-js@^1.3.1:
base64-js@1.5.1, base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
Expand Down Expand Up @@ -17482,6 +17498,11 @@ unc-path-regex@^0.1.2:
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo=

[email protected]:
version "4.1.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db"
integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==

unherit@^1.0.4:
version "1.1.3"
resolved "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz"
Expand Down

0 comments on commit 134879e

Please sign in to comment.