feat: 🎸 Feature toggle context

This commit is contained in:
Viktor Sarström 2021-11-22 10:47:11 +01:00
parent aa98a400d9
commit ce056821a1
11 changed files with 87 additions and 62 deletions

View File

@ -1,8 +1,12 @@
import * as eva from '@eva-design/eva'
import AsyncStorage from '@react-native-async-storage/async-storage'
import CookieManager from '@react-native-cookies/cookies'
import initSkolplattformen from '@skolplattformen/api-skolplattformen'
import initHjarntorget from '@skolplattformen/api-hjarntorget'
import initSkolplattformen, {
features as featuresSkolplattformen,
} from '@skolplattformen/api-skolplattformen'
import initHjarntorget, {
features as featuresHjarntorget,
} from '@skolplattformen/api-hjarntorget'
import { ApiProvider } from '@skolplattformen/hooks'
import { ApplicationProvider, IconRegistry } from '@ui-kitten/components'
@ -19,6 +23,7 @@ import useSettingsStorage from './hooks/useSettingsStorage'
import { translations } from './utils/translation'
import { Reporter } from '@skolplattformen/hooks'
import { Api } from '@skolplattformen/api'
import { FeatureProvider } from './context/feature/featureContext'
const ApiList = new Map<string, Api>([
// @ts-expect-error Why is fetch failing here?
@ -77,27 +82,31 @@ export default () => {
const api = ApiList.get('goteborg-hjarntorget')!
return (
<SchoolPlatformProvider>
<ApiProvider api={api} storage={AsyncStorage} reporter={reporter}>
<SafeAreaProvider>
<StatusBar
backgroundColor={colorScheme === 'dark' ? '#2E3137' : '#FFF'}
barStyle={colorScheme === 'dark' ? 'light-content' : 'dark-content'}
translucent
/>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider
{...eva}
// @ts-expect-error Unknown error
customMapping={customMapping}
theme={colorScheme === 'dark' ? darkTheme : lightTheme}
>
<LanguageProvider cache={true} data={translations}>
<AppNavigator />
</LanguageProvider>
</ApplicationProvider>
</SafeAreaProvider>
</ApiProvider>
</SchoolPlatformProvider>
<FeatureProvider features={featuresHjarntorget}>
<SchoolPlatformProvider>
<ApiProvider api={api} storage={AsyncStorage} reporter={reporter}>
<SafeAreaProvider>
<StatusBar
backgroundColor={colorScheme === 'dark' ? '#2E3137' : '#FFF'}
barStyle={
colorScheme === 'dark' ? 'light-content' : 'dark-content'
}
translucent
/>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider
{...eva}
// @ts-expect-error Unknown error
customMapping={customMapping}
theme={colorScheme === 'dark' ? darkTheme : lightTheme}
>
<LanguageProvider cache={true} data={translations}>
<AppNavigator />
</LanguageProvider>
</ApplicationProvider>
</SafeAreaProvider>
</ApiProvider>
</SchoolPlatformProvider>
</FeatureProvider>
)
}

View File

@ -23,6 +23,7 @@ import {
} from 'react-native'
import { schema } from '../app.json'
import { SchoolPlatformContext } from '../context/schoolPlatform/schoolPlatformContext'
import { useFeature } from '../hooks/useFeature'
import useSettingsStorage from '../hooks/useSettingsStorage'
import { useTranslation } from '../hooks/useTranslation'
import { Layout as LayoutStyle, Sizing, Typography } from '../styles'
@ -57,6 +58,7 @@ export const Login = () => {
const [loginMethodIndex, setLoginMethodIndex] =
useSettingsStorage('loginMethodIndex')
const loginBankIdSameDevice = useFeature('LOGIN_BANK_ID_SAME_DEVICE')
const { currentSchoolPlatform, changeSchoolPlatform } = useContext(
SchoolPlatformContext
)
@ -66,11 +68,14 @@ export const Login = () => {
const valid = Personnummer.valid(personalIdNumber)
const loginMethods = [
t('auth.bankid.OpenOnThisDevice'),
t('auth.bankid.OpenOnAnotherDevice'),
t('auth.loginAsTestUser'),
]
if (loginBankIdSameDevice) {
loginMethods.unshift(t('auth.bankid.OpenOnThisDevice'))
}
const schoolPlatforms = [
{
id: 'stockholm-skolplattformen',

View File

@ -0,0 +1,18 @@
import { Features, FeatureType } from '@skolplattformen/api'
import React from 'react'
export const FeatureFlagsContext = React.createContext<Features>({
LOGIN_BANK_ID_SAME_DEVICE: false,
})
interface Props {
features: Features
}
export const FeatureProvider: React.FC<Props> = (props) => {
return (
<FeatureFlagsContext.Provider value={props.features} {...props}>
{props.children}
</FeatureFlagsContext.Provider>
)
}

View File

@ -0,0 +1,12 @@
import React from 'react'
import { FeatureType, Features } from '@skolplattformen/api'
import { FeatureFlagsContext } from '../context/feature/featureContext'
export const useFeature = (name: FeatureType) => {
const features = React.useContext<Features>(FeatureFlagsContext)
if (features === null) {
throw new Error('You must wrap your components in a FeatureProvider.')
}
return features[name]
}

View File

@ -1,5 +1,5 @@
import { Feature } from '@skolplattformen/api'
import { Features } from '@skolplattformen/api'
export const features: Feature[] = [
{ name: 'login', enabled: true },
]
export const features: Features = {
LOGIN_BANK_ID_SAME_DEVICE: false
}

View File

@ -3,6 +3,7 @@ import { Api, FetcherOptions, Fetch, RNCookieManager,
ToughCookieJar,
wrapReactNativeCookieManager,
wrapToughCookie } from '@skolplattformen/api'
export { features } from './features'
const init = (
fetchImpl: Fetch,

View File

@ -0,0 +1,5 @@
import { Features } from '@skolplattformen/api'
export const features: Features = {
LOGIN_BANK_ID_SAME_DEVICE: true
}

View File

@ -3,6 +3,7 @@ import { Api, FetcherOptions, Fetch, RNCookieManager,
ToughCookieJar,
wrapReactNativeCookieManager,
wrapToughCookie } from '@skolplattformen/api'
export { features } from './features'
const init = (
fetchImpl: Fetch,

View File

@ -1,4 +1,5 @@
export interface Feature {
name: string;
enabled: boolean;
}
export interface Features {
LOGIN_BANK_ID_SAME_DEVICE: boolean;
}
export type FeatureType = keyof Features;

View File

@ -9,9 +9,9 @@ export {
RNCookieManager,
ToughCookieJar,
wrapReactNativeCookieManager,
wrapToughCookie,
wrapToughCookie,
} from './cookies'
export { URLSearchParams } from './URLSearchParams'
export { wrap };
export { Feature } from './features'
export { FeatureType, Features } from './features'

View File

@ -1,27 +0,0 @@
import { Feature } from '@skolplattformen/api'
import React from 'react'
const FeatureFlagsContext = React.createContext<Feature[]>([])
interface Props {
features: Feature[]
}
export const FeatureProvider: React.FC<Props> = (props) => {
return (
<FeatureFlagsContext.Provider value={props.features} {...props}>
{props.children}
</FeatureFlagsContext.Provider>
)
}
export const useFeature = (name: string) => {
const features = React.useContext<Feature[]>(FeatureFlagsContext)
if (features === null) {
throw new Error('You must wrap your components in a FeatureProvider.')
}
const feature = features.find((f) => f.name === name)
return feature && feature.enabled
}